forgecad 0.10.5 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{AdminPage-raksfnNA.js → AdminPage-B1nIvqLS.js} +1 -1
- package/dist/assets/{BenchmarkPage-DP3RxhPs.js → BenchmarkPage-YZJbw5nd.js} +1 -1
- package/dist/assets/{BlogPage-D7Dos-vl.js → BlogPage-DIWRApKS.js} +1 -1
- package/dist/assets/{DocsPage-DO1kvBns.js → DocsPage-ClL6X1hR.js} +2 -22
- package/dist/assets/{EditorApp-DQJmcmRT.js → EditorApp-CYBDvSyT.js} +575 -119
- package/dist/assets/{EmbedViewer-DFDUhOma.js → EmbedViewer-Dmfu_LIw.js} +2 -2
- package/dist/assets/{LandingPageProofDriven-DbE_tp8-.js → LandingPageProofDriven-XYTiYxfM.js} +1 -1
- package/dist/assets/{LegalPage-CominSso.js → LegalPage-D5Z3CscF.js} +1 -1
- package/dist/assets/{PricingPage-CcVIN9yj.js → PricingPage-BP4lIGio.js} +1 -1
- package/dist/assets/{SettingsPage-DLWcP289.js → SettingsPage-D3bcPBsC.js} +1 -1
- package/dist/assets/{app-xW3hOdq9.js → app-BKjogwIZ.js} +2192 -231
- package/dist/assets/{backendInit-mDHk97u7.js → backendInit-6a9-ilom.js} +76448 -75066
- package/dist/assets/cli/{render--SIU27W_.js → render-CMNudGb0.js} +3 -3
- package/dist/assets/{constructionHistoryWorker-uEe_Q7Kg.js → constructionHistoryWorker-BuZgc606.js} +6985 -6706
- package/dist/assets/{evalWorker-BqyDHDcI.js → evalWorker-DQ82ueGu.js} +40862 -39497
- package/dist/assets/{inspectWorker-UXMxlcR8.js → inspectWorker-Cuby2qfT.js} +2078 -478
- package/dist/assets/{jointPose-bYMlwU3v.js → jointPose-CFql5I-u.js} +1 -1
- package/dist/assets/{manifold-CyOV5B9S.js → manifold-02pmr7O7.js} +2 -2
- package/dist/assets/{manifold-BR7UYI4P.js → manifold-C6KU0oII.js} +1 -1
- package/dist/assets/{manifold-D4d5NQst.js → manifold-P1yF3GKn.js} +1 -1
- package/dist/assets/{reportWorker-DsaICZsn.js → reportWorker-kg065BVL.js} +85183 -78309
- package/dist/cli/render.html +1 -1
- package/dist/docs/index.html +2 -2
- package/dist/docs-raw/AI/usage.md +6 -8
- package/dist/docs-raw/CLI.md +10 -10
- package/dist/docs-raw/component-model.md +28 -9
- package/dist/docs-raw/generated/concepts.md +13 -4
- package/dist/docs-raw/generated/core.md +244 -56
- package/dist/docs-raw/generated/curves.md +13 -0
- package/dist/docs-raw/generated/runtime-names.md +2 -2
- package/dist/docs-raw/guides/inspection-bundles.md +1 -1
- package/dist/docs-raw/guides/structural-fea.md +11 -0
- package/dist/docs-raw/skills/forgecad-build-model.md +70 -147
- package/dist/docs-raw/skills/forgecad-image-prompt.md +1 -1
- package/dist/docs-raw/skills/forgecad-project-sync.md +3 -3
- package/dist/docs-raw/skills/forgecad-reconstruct-cad-file.md +2 -2
- package/dist/docs-raw/skills/forgecad-reconstruct-from-images.md +4 -5
- package/dist/docs-raw/skills/forgecad.md +3 -1
- package/dist/docs-raw/skills/index.md +1 -5
- package/dist/docs-raw/welcome.md +3 -4
- package/dist/index.html +1 -1
- package/dist/llms.txt +1 -2
- package/dist/sitemap.xml +15 -15
- package/dist-cli/{check-compiler-7YAHVXYM.js → check-compiler-UJWUEIDC.js} +1 -1
- package/dist-cli/{check-query-propagation-ZRR6IOJW.js → check-query-propagation-O2EPDJSY.js} +1 -1
- package/dist-cli/{chunk-VNM67DIV.js → chunk-MNDROM7T.js} +77145 -75767
- package/dist-cli/forgecad.js +1145 -441
- package/dist-skill/CONTEXT.md +429 -64
- package/dist-skill/SKILL.md +3 -1
- package/dist-skill/docs/API/core/concepts.md +31 -4
- package/dist-skill/docs/CLI.md +10 -10
- package/dist-skill/docs/generated/core.md +240 -57
- package/dist-skill/docs/generated/curves.md +13 -0
- package/dist-skill/docs/generated/runtime-names.md +2 -2
- package/dist-skill/docs/guides/inspection-bundles.md +1 -1
- package/dist-skill/docs/guides/manual-parameters.md +130 -0
- package/dist-skill/docs/guides/structural-fea.md +11 -0
- package/dist-skill/library/README.md +0 -4
- package/dist-skill/library/forgecad-build-model/SKILL.md +57 -150
- package/dist-skill/library/forgecad-build-model/references/inspection-feedback.md +58 -0
- package/dist-skill/library/forgecad-build-model/references/module-contracts.md +53 -0
- package/dist-skill/library/forgecad-build-model/references/parameter-controls.md +22 -0
- package/dist-skill/library/forgecad-build-model/references/readiness-review.md +43 -0
- package/dist-skill/library/forgecad-build-model/references/simulation-feedback.md +49 -0
- package/dist-skill/library/forgecad-build-model/references/stage-1-design-intent.md +21 -0
- package/dist-skill/library/forgecad-build-model/references/stage-2-architecture-plan.md +23 -0
- package/dist-skill/library/forgecad-build-model/references/stage-3-build-slices.md +39 -0
- package/dist-skill/library/forgecad-build-model/references/stage-4-feedback-iteration.md +24 -0
- package/dist-skill/library/forgecad-build-model/references/stage-5-readiness-package.md +34 -0
- package/dist-skill/library/forgecad-image-prompt/SKILL.md +1 -1
- package/dist-skill/library/forgecad-project-sync/SKILL.md +3 -3
- package/dist-skill/library/forgecad-reconstruct-cad-file/SKILL.md +2 -2
- package/dist-skill/library/forgecad-reconstruct-from-images/SKILL.md +4 -5
- package/dist-skill/website/skills/forgecad-build-model.md +70 -147
- package/dist-skill/website/skills/forgecad-image-prompt.md +1 -1
- package/dist-skill/website/skills/forgecad-project-sync.md +3 -3
- package/dist-skill/website/skills/forgecad-reconstruct-cad-file.md +2 -2
- package/dist-skill/website/skills/forgecad-reconstruct-from-images.md +4 -5
- package/dist-skill/website/skills/forgecad.md +3 -1
- package/dist-skill/website/skills/index.md +1 -5
- package/examples/api/param-path2d.forge.js +65 -0
- package/examples/api/param-placement2d.forge.js +80 -0
- package/examples/api/param-spline2d-g-continuity.forge.js +57 -0
- package/examples/api/spoon-full-tang-handle.forge.js +57 -17
- package/examples/api/surface-variable-thickness-panel.forge.js +62 -0
- package/examples/mechanical/airplane-propeller.forge.js +81 -28
- package/package.json +2 -2
- package/dist/docs-raw/skills/forgecad-design-spec.md +0 -145
- package/dist/docs-raw/skills/forgecad-grade-model.md +0 -84
- package/dist/docs-raw/skills/forgecad-inspect-model.md +0 -80
- package/dist/docs-raw/skills/forgecad-verify-mujoco.md +0 -78
- package/dist-skill/library/forgecad-design-spec/SKILL.md +0 -132
- package/dist-skill/library/forgecad-design-spec/references/default-profiles.md +0 -99
- package/dist-skill/library/forgecad-design-spec/references/master-prompt.md +0 -73
- package/dist-skill/library/forgecad-grade-model/SKILL.md +0 -72
- package/dist-skill/library/forgecad-grade-model/agents/openai.yaml +0 -4
- package/dist-skill/library/forgecad-inspect-model/SKILL.md +0 -68
- package/dist-skill/library/forgecad-verify-mujoco/SKILL.md +0 -66
- package/dist-skill/website/skills/forgecad-design-spec.md +0 -145
- package/dist-skill/website/skills/forgecad-grade-model.md +0 -84
- package/dist-skill/website/skills/forgecad-inspect-model.md +0 -80
- package/dist-skill/website/skills/forgecad-verify-mujoco.md +0 -78
- /package/dist-skill/library/{forgecad-verify-mujoco → forgecad-build-model}/scripts/mujoco_verify.py +0 -0
- /package/dist-skill/library/{forgecad-inspect-model → forgecad-build-model/scripts}/summarize_manifest.py +0 -0
package/dist-cli/forgecad.js
CHANGED
|
@@ -141,7 +141,7 @@ import {
|
|
|
141
141
|
updateConstraintValue,
|
|
142
142
|
validateSimulationModel,
|
|
143
143
|
wrapOCCTShapeBackend
|
|
144
|
-
} from "./chunk-
|
|
144
|
+
} from "./chunk-MNDROM7T.js";
|
|
145
145
|
|
|
146
146
|
// cli/forgecad.ts
|
|
147
147
|
import { Command, Flags, Help, flush as flushOclif, handle as handleOclif, run as runOclif } from "@oclif/core";
|
|
@@ -2896,9 +2896,9 @@ function captureSnapshot(sketch) {
|
|
|
2896
2896
|
};
|
|
2897
2897
|
}
|
|
2898
2898
|
function loadSnapshots() {
|
|
2899
|
-
const
|
|
2900
|
-
if (!existsSync(
|
|
2901
|
-
return JSON.parse(readFileSync2(
|
|
2899
|
+
const path7 = join2(SNAPSHOT_DIR, "constraint-snapshots.json");
|
|
2900
|
+
if (!existsSync(path7)) return {};
|
|
2901
|
+
return JSON.parse(readFileSync2(path7, "utf-8"));
|
|
2902
2902
|
}
|
|
2903
2903
|
function saveSnapshots(data) {
|
|
2904
2904
|
if (!existsSync(SNAPSHOT_DIR)) mkdirSync(SNAPSHOT_DIR, { recursive: true });
|
|
@@ -4076,9 +4076,9 @@ function exactRoute(note) {
|
|
|
4076
4076
|
function facetedRoute(blocker, note) {
|
|
4077
4077
|
return { kind: "faceted", blocker, note };
|
|
4078
4078
|
}
|
|
4079
|
-
function partExample(family,
|
|
4079
|
+
function partExample(family, path7, route, note, primaryShapes) {
|
|
4080
4080
|
return {
|
|
4081
|
-
path:
|
|
4081
|
+
path: path7,
|
|
4082
4082
|
family,
|
|
4083
4083
|
class: "part",
|
|
4084
4084
|
validation: "part-runtime",
|
|
@@ -4087,9 +4087,9 @@ function partExample(family, path5, route, note, primaryShapes) {
|
|
|
4087
4087
|
note
|
|
4088
4088
|
};
|
|
4089
4089
|
}
|
|
4090
|
-
function assemblyExample(
|
|
4090
|
+
function assemblyExample(path7, note, expect4) {
|
|
4091
4091
|
return {
|
|
4092
|
-
path:
|
|
4092
|
+
path: path7,
|
|
4093
4093
|
family: "non-part",
|
|
4094
4094
|
class: "assembly",
|
|
4095
4095
|
validation: "assembly-runtime",
|
|
@@ -4097,9 +4097,9 @@ function assemblyExample(path5, note, expect4) {
|
|
|
4097
4097
|
expect: expect4
|
|
4098
4098
|
};
|
|
4099
4099
|
}
|
|
4100
|
-
function runtimeSceneExample(
|
|
4100
|
+
function runtimeSceneExample(path7, note, expect4) {
|
|
4101
4101
|
return {
|
|
4102
|
-
path:
|
|
4102
|
+
path: path7,
|
|
4103
4103
|
family: "non-part",
|
|
4104
4104
|
class: "runtime-scene",
|
|
4105
4105
|
validation: "runtime-scene",
|
|
@@ -4107,9 +4107,9 @@ function runtimeSceneExample(path5, note, expect4) {
|
|
|
4107
4107
|
expect: expect4
|
|
4108
4108
|
};
|
|
4109
4109
|
}
|
|
4110
|
-
function sketchExample(
|
|
4110
|
+
function sketchExample(path7, note, expect4) {
|
|
4111
4111
|
return {
|
|
4112
|
-
path:
|
|
4112
|
+
path: path7,
|
|
4113
4113
|
family: "non-part",
|
|
4114
4114
|
class: "sketch",
|
|
4115
4115
|
validation: "sketch-svg",
|
|
@@ -4117,9 +4117,9 @@ function sketchExample(path5, note, expect4) {
|
|
|
4117
4117
|
expect: expect4
|
|
4118
4118
|
};
|
|
4119
4119
|
}
|
|
4120
|
-
function experimentalExample(
|
|
4120
|
+
function experimentalExample(path7, blocker, taskRef, note) {
|
|
4121
4121
|
return {
|
|
4122
|
-
path:
|
|
4122
|
+
path: path7,
|
|
4123
4123
|
family: "experimental",
|
|
4124
4124
|
class: "experimental",
|
|
4125
4125
|
validation: "experimental-runtime",
|
|
@@ -4139,6 +4139,8 @@ var API_EXACT_PART_PATHS = [
|
|
|
4139
4139
|
"examples/api/folded-service-panel-cover.forge.js",
|
|
4140
4140
|
"examples/api/group-vs-union.forge.js",
|
|
4141
4141
|
"examples/api/patterns.forge.js",
|
|
4142
|
+
"examples/api/param-spline2d-g-continuity.forge.js",
|
|
4143
|
+
"examples/api/param-path2d.forge.js",
|
|
4142
4144
|
"examples/api/text2d-basics.forge.js",
|
|
4143
4145
|
"examples/api/text2d-font.forge.js",
|
|
4144
4146
|
"examples/api/extrude-options.forge.js",
|
|
@@ -4157,6 +4159,11 @@ var API_FACETED_PARTS = [
|
|
|
4157
4159
|
path: "examples/api/profile-2020-b-slot6.forge.js",
|
|
4158
4160
|
blocker: "The direct 3D profile helper still lowers through segmented profile geometry, so the extrusion must stay on the faceted route for now.",
|
|
4159
4161
|
note: "The sketch half of the example remains exact-capable; the 3D helper is the intentional blocker."
|
|
4162
|
+
},
|
|
4163
|
+
{
|
|
4164
|
+
path: "examples/api/surface-variable-thickness-panel.forge.js",
|
|
4165
|
+
blocker: "Variable-thickness surface shells lower through sampled UV normal offsets because exact B-rep variable offsets are not available yet.",
|
|
4166
|
+
note: "The example validates the new Thickness.grid() API and the sampled watertight shell path."
|
|
4160
4167
|
}
|
|
4161
4168
|
];
|
|
4162
4169
|
var API_RECOVERED_FACETED_PARTS = [
|
|
@@ -4173,7 +4180,7 @@ var COMPILER_CORPUS_PATHS = [
|
|
|
4173
4180
|
];
|
|
4174
4181
|
var API_AND_CORPUS_EXAMPLE_MANIFEST = [
|
|
4175
4182
|
...API_EXACT_PART_PATHS.map(
|
|
4176
|
-
(
|
|
4183
|
+
(path7) => partExample("api-parts", path7, exactRoute("This API example now stays inside the defended exact-route subset."))
|
|
4177
4184
|
),
|
|
4178
4185
|
...API_FACETED_PARTS.map(
|
|
4179
4186
|
(entry) => partExample("api-parts", entry.path, facetedRoute(entry.blocker, entry.note), void 0, entry.primaryShapes)
|
|
@@ -4182,7 +4189,7 @@ var API_AND_CORPUS_EXAMPLE_MANIFEST = [
|
|
|
4182
4189
|
(entry) => partExample("api-parts", entry.path, facetedRoute(entry.blocker, entry.note), void 0, entry.primaryShapes)
|
|
4183
4190
|
),
|
|
4184
4191
|
...COMPILER_CORPUS_PATHS.map(
|
|
4185
|
-
(
|
|
4192
|
+
(path7) => partExample("compiler-corpus", path7, exactRoute("The compiler corpus is the defended ordinary-part exact subset and must stay exact."))
|
|
4186
4193
|
)
|
|
4187
4194
|
];
|
|
4188
4195
|
|
|
@@ -4936,8 +4943,8 @@ var VALIDATION_PATH_BY_CLASS = {
|
|
|
4936
4943
|
sketch: "sketch-svg",
|
|
4937
4944
|
experimental: "experimental-runtime"
|
|
4938
4945
|
};
|
|
4939
|
-
function normalizeManifestPath(
|
|
4940
|
-
return
|
|
4946
|
+
function normalizeManifestPath(path7) {
|
|
4947
|
+
return path7.replaceAll("\\", "/");
|
|
4941
4948
|
}
|
|
4942
4949
|
function parseArgs(argv) {
|
|
4943
4950
|
let profile = "smoke";
|
|
@@ -4977,25 +4984,25 @@ function parseArgs(argv) {
|
|
|
4977
4984
|
}
|
|
4978
4985
|
function assertManifestCoverage(entries) {
|
|
4979
4986
|
const manifestPaths = entries.map((entry) => entry.path);
|
|
4980
|
-
const duplicates = manifestPaths.filter((
|
|
4987
|
+
const duplicates = manifestPaths.filter((path7, index) => manifestPaths.indexOf(path7) !== index);
|
|
4981
4988
|
const duplicatePaths = [...new Set(duplicates)].sort();
|
|
4982
4989
|
const discovered = listExampleArtifacts();
|
|
4983
4990
|
const _manifestSet = new Set(manifestPaths);
|
|
4984
4991
|
const discoveredSet = new Set(discovered);
|
|
4985
4992
|
const unclassified = [];
|
|
4986
|
-
const missingFiles = manifestPaths.filter((
|
|
4993
|
+
const missingFiles = manifestPaths.filter((path7) => !discoveredSet.has(path7));
|
|
4987
4994
|
const issues = [];
|
|
4988
4995
|
if (duplicatePaths.length > 0) {
|
|
4989
4996
|
issues.push(`Duplicate manifest entries:
|
|
4990
|
-
${duplicatePaths.map((
|
|
4997
|
+
${duplicatePaths.map((path7) => ` - ${path7}`).join("\n")}`);
|
|
4991
4998
|
}
|
|
4992
4999
|
if (unclassified.length > 0) {
|
|
4993
5000
|
issues.push(`Unclassified example artifacts:
|
|
4994
|
-
${unclassified.map((
|
|
5001
|
+
${unclassified.map((path7) => ` - ${path7}`).join("\n")}`);
|
|
4995
5002
|
}
|
|
4996
5003
|
if (missingFiles.length > 0) {
|
|
4997
5004
|
issues.push(`Manifest entries that point at missing files:
|
|
4998
|
-
${missingFiles.map((
|
|
5005
|
+
${missingFiles.map((path7) => ` - ${path7}`).join("\n")}`);
|
|
4999
5006
|
}
|
|
5000
5007
|
if (issues.length > 0) {
|
|
5001
5008
|
throw new Error(`Example manifest coverage failed.
|
|
@@ -5031,7 +5038,7 @@ function selectEntries(entries, args) {
|
|
|
5031
5038
|
}
|
|
5032
5039
|
if (args.examples.length > 0) {
|
|
5033
5040
|
const exampleSet = new Set(args.examples);
|
|
5034
|
-
const missingExamples = args.examples.filter((
|
|
5041
|
+
const missingExamples = args.examples.filter((path7) => !entries.some((entry) => entry.path === path7));
|
|
5035
5042
|
if (missingExamples.length > 0) {
|
|
5036
5043
|
throw new Error(`Unknown manifest example path(s): ${missingExamples.join(", ")}`);
|
|
5037
5044
|
}
|
|
@@ -5500,9 +5507,9 @@ import { basename as basename3 } from "path";
|
|
|
5500
5507
|
var RENDERABLE_INPUT_EXTENSIONS = [".forge.js", ".js", ".stl", ".obj", ".3mf", ".step", ".stp"];
|
|
5501
5508
|
var EXACT_EXPORT_INPUT_EXTENSIONS = [".forge.js", ".js", ".step", ".stp"];
|
|
5502
5509
|
var SCRIPT_INPUT_EXTENSIONS = [".forge.js", ".js"];
|
|
5503
|
-
function requireInputPaths(inputPaths,
|
|
5510
|
+
function requireInputPaths(inputPaths, usage28) {
|
|
5504
5511
|
if (inputPaths.length === 0) {
|
|
5505
|
-
throw new Error(
|
|
5512
|
+
throw new Error(usage28);
|
|
5506
5513
|
}
|
|
5507
5514
|
return inputPaths;
|
|
5508
5515
|
}
|
|
@@ -5554,16 +5561,16 @@ function hasSupportedExtension(inputPath, extensions) {
|
|
|
5554
5561
|
import { existsSync as existsSync3, readFileSync as readFileSync4, statSync as statSync3 } from "fs";
|
|
5555
5562
|
import { homedir } from "os";
|
|
5556
5563
|
import { resolve as resolve5 } from "path";
|
|
5557
|
-
function expandUserPath(
|
|
5558
|
-
if (
|
|
5559
|
-
if (
|
|
5560
|
-
return resolve5(
|
|
5564
|
+
function expandUserPath(path7) {
|
|
5565
|
+
if (path7 === "~") return homedir();
|
|
5566
|
+
if (path7.startsWith("~/")) return resolve5(homedir(), path7.slice(2));
|
|
5567
|
+
return resolve5(path7);
|
|
5561
5568
|
}
|
|
5562
5569
|
function looksLikePath(value) {
|
|
5563
5570
|
return value.endsWith(".json") || value.includes("/") || value.includes("\\") || value.startsWith(".") || value.startsWith("~");
|
|
5564
5571
|
}
|
|
5565
|
-
function readJsonFile(
|
|
5566
|
-
const resolved = expandUserPath(
|
|
5572
|
+
function readJsonFile(path7, flag) {
|
|
5573
|
+
const resolved = expandUserPath(path7);
|
|
5567
5574
|
if (!existsSync3(resolved)) {
|
|
5568
5575
|
throw new Error(`${flag} file not found: ${resolved}`);
|
|
5569
5576
|
}
|
|
@@ -5576,8 +5583,8 @@ function readJsonFile(path5, flag) {
|
|
|
5576
5583
|
}
|
|
5577
5584
|
return content;
|
|
5578
5585
|
}
|
|
5579
|
-
function readCliJsonFile(
|
|
5580
|
-
return readJsonFile(
|
|
5586
|
+
function readCliJsonFile(path7, flag) {
|
|
5587
|
+
return readJsonFile(path7, flag);
|
|
5581
5588
|
}
|
|
5582
5589
|
function readCliJsonOrFile(value, flag) {
|
|
5583
5590
|
const trimmed = value.trim();
|
|
@@ -5702,21 +5709,21 @@ function extractImports(code) {
|
|
|
5702
5709
|
let m;
|
|
5703
5710
|
const forgeRe = new RegExp(FORGE_IMPORT_RE.source, FORGE_IMPORT_RE.flags);
|
|
5704
5711
|
while ((m = forgeRe.exec(code)) !== null) {
|
|
5705
|
-
const
|
|
5712
|
+
const path7 = m[1] ?? m[2];
|
|
5706
5713
|
const fn = m[0].match(
|
|
5707
5714
|
/\b(importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.image|Import\.svgSketch|Import\.dxfSketch|compareWith)/
|
|
5708
5715
|
)?.[1];
|
|
5709
|
-
add3(
|
|
5716
|
+
add3(path7, kindMap[fn] ?? "forgeMesh");
|
|
5710
5717
|
}
|
|
5711
5718
|
const reqRe = new RegExp(REQUIRE_RE.source, REQUIRE_RE.flags);
|
|
5712
5719
|
while ((m = reqRe.exec(code)) !== null) {
|
|
5713
|
-
const
|
|
5714
|
-
add3(
|
|
5720
|
+
const path7 = m[1] ?? m[2];
|
|
5721
|
+
add3(path7, "require");
|
|
5715
5722
|
}
|
|
5716
5723
|
const esRe = new RegExp(ES_IMPORT_RE.source, ES_IMPORT_RE.flags);
|
|
5717
5724
|
while ((m = esRe.exec(code)) !== null) {
|
|
5718
|
-
const
|
|
5719
|
-
add3(
|
|
5725
|
+
const path7 = m[1] ?? m[2];
|
|
5726
|
+
add3(path7, "esImport");
|
|
5720
5727
|
}
|
|
5721
5728
|
return refs;
|
|
5722
5729
|
}
|
|
@@ -5742,8 +5749,8 @@ function resolveRelative(fromFile, importPath) {
|
|
|
5742
5749
|
}
|
|
5743
5750
|
return resolved.join("/");
|
|
5744
5751
|
}
|
|
5745
|
-
function isCompareBinaryPath(
|
|
5746
|
-
return /\.(?:stl|obj|3mf|step|stp)$/i.test(
|
|
5752
|
+
function isCompareBinaryPath(path7) {
|
|
5753
|
+
return /\.(?:stl|obj|3mf|step|stp)$/i.test(path7);
|
|
5747
5754
|
}
|
|
5748
5755
|
function collectDependencies(entryFile, allFiles) {
|
|
5749
5756
|
const codeFiles = {};
|
|
@@ -5801,18 +5808,18 @@ function collectDependencies(entryFile, allFiles) {
|
|
|
5801
5808
|
var DIRECT_MESH_EXTS = /* @__PURE__ */ new Set([".stl", ".obj", ".3mf"]);
|
|
5802
5809
|
var DIRECT_STEP_EXTS = /* @__PURE__ */ new Set([".step", ".stp"]);
|
|
5803
5810
|
var DIRECT_CAD_EXTS = /* @__PURE__ */ new Set([...DIRECT_MESH_EXTS, ...DIRECT_STEP_EXTS]);
|
|
5804
|
-
function directCadInputKind(
|
|
5805
|
-
const ext = extname(
|
|
5811
|
+
function directCadInputKind(path7) {
|
|
5812
|
+
const ext = extname(path7).toLowerCase();
|
|
5806
5813
|
if (DIRECT_MESH_EXTS.has(ext)) return "mesh";
|
|
5807
5814
|
if (DIRECT_STEP_EXTS.has(ext)) return "step";
|
|
5808
5815
|
return null;
|
|
5809
5816
|
}
|
|
5810
|
-
function replaceCliInputExtension(
|
|
5811
|
-
const lower2 =
|
|
5817
|
+
function replaceCliInputExtension(path7, newExt) {
|
|
5818
|
+
const lower2 = path7.toLowerCase();
|
|
5812
5819
|
for (const ext of [".forge.js", ".js", ".svg", ".dxf", ...DIRECT_CAD_EXTS]) {
|
|
5813
|
-
if (lower2.endsWith(ext)) return
|
|
5820
|
+
if (lower2.endsWith(ext)) return path7.slice(0, -ext.length) + newExt;
|
|
5814
5821
|
}
|
|
5815
|
-
return
|
|
5822
|
+
return path7 + newExt;
|
|
5816
5823
|
}
|
|
5817
5824
|
function buildDirectCadImportScript(fileName, kind) {
|
|
5818
5825
|
const importFn = kind === "step" ? "Import.step" : "Import.mesh";
|
|
@@ -5823,10 +5830,10 @@ function buildDirectCadImportScript(fileName, kind) {
|
|
|
5823
5830
|
`return [{ name: ${JSON.stringify(objectName)}, shape: imported }];`
|
|
5824
5831
|
].join("\n");
|
|
5825
5832
|
}
|
|
5826
|
-
function loadCliScriptInput(inputPath) {
|
|
5833
|
+
function loadCliScriptInput(inputPath, options = {}) {
|
|
5827
5834
|
const sourcePath = resolve6(inputPath);
|
|
5828
5835
|
const directCadKind = directCadInputKind(sourcePath);
|
|
5829
|
-
const { projectRoot, allFiles, fileName, readBinaryFile } = collectProjectFiles(inputPath);
|
|
5836
|
+
const { projectRoot, allFiles, fileName, readBinaryFile } = collectProjectFiles(inputPath, options);
|
|
5830
5837
|
const directThreeMfAnalysis = directCadKind === "mesh" && extname(fileName).toLowerCase() === ".3mf" ? analyze3mf(readBinaryFile(fileName)) : void 0;
|
|
5831
5838
|
if (!directCadKind) {
|
|
5832
5839
|
return {
|
|
@@ -7787,8 +7794,8 @@ function parseHideArg(argv, idx) {
|
|
|
7787
7794
|
if (values.length === 0) throw new Error("--hide requires at least one object name.");
|
|
7788
7795
|
return { value: values, nextIndex: idx + 1 };
|
|
7789
7796
|
}
|
|
7790
|
-
function replaceRenderableInputExtension(
|
|
7791
|
-
return replaceCliInputExtension(
|
|
7797
|
+
function replaceRenderableInputExtension(path7, newExt) {
|
|
7798
|
+
return replaceCliInputExtension(path7, newExt);
|
|
7792
7799
|
}
|
|
7793
7800
|
function parseRenderCliOptions(argv) {
|
|
7794
7801
|
if (argv.length === 0 || argv.includes("-h") || argv.includes("--help")) {
|
|
@@ -8756,8 +8763,8 @@ function collectRenderProjectFiles(scriptPath) {
|
|
|
8756
8763
|
fileName: pathRelativeTo(projectRoot, absScript)
|
|
8757
8764
|
};
|
|
8758
8765
|
}
|
|
8759
|
-
function toPortablePath(
|
|
8760
|
-
return
|
|
8766
|
+
function toPortablePath(path7) {
|
|
8767
|
+
return path7.split("\\").join("/");
|
|
8761
8768
|
}
|
|
8762
8769
|
function pathRelativeTo(from, to) {
|
|
8763
8770
|
const rel = relative3(from, to);
|
|
@@ -8824,10 +8831,10 @@ function readPackageVersion() {
|
|
|
8824
8831
|
}
|
|
8825
8832
|
function buildViewPathEntries(pathsByView) {
|
|
8826
8833
|
return Object.fromEntries(
|
|
8827
|
-
Object.entries(pathsByView ?? {}).map(([view,
|
|
8834
|
+
Object.entries(pathsByView ?? {}).map(([view, path7]) => [
|
|
8828
8835
|
view,
|
|
8829
8836
|
{
|
|
8830
|
-
path:
|
|
8837
|
+
path: path7
|
|
8831
8838
|
}
|
|
8832
8839
|
])
|
|
8833
8840
|
);
|
|
@@ -10916,8 +10923,8 @@ function projectDesignHistory(trace, options = {}) {
|
|
|
10916
10923
|
function plural(count, singular) {
|
|
10917
10924
|
return `${count} ${singular}${count === 1 ? "" : "s"}`;
|
|
10918
10925
|
}
|
|
10919
|
-
function pathLabel(
|
|
10920
|
-
return
|
|
10926
|
+
function pathLabel(path7, fallback) {
|
|
10927
|
+
return path7.length > 0 ? path7.join(" > ") : fallback;
|
|
10921
10928
|
}
|
|
10922
10929
|
function sourceLabel2(step) {
|
|
10923
10930
|
if (!step.sourceSpan) return "";
|
|
@@ -11222,13 +11229,13 @@ function childPath(parent, childName, index) {
|
|
|
11222
11229
|
const label = childName ?? `child ${index + 1}`;
|
|
11223
11230
|
return parent ? `${parent}.${label}` : label;
|
|
11224
11231
|
}
|
|
11225
|
-
function partShapeLeaves(part,
|
|
11232
|
+
function partShapeLeaves(part, path7 = "") {
|
|
11226
11233
|
if (part instanceof Shape) {
|
|
11227
11234
|
const bbox = partBoundingBox(part);
|
|
11228
11235
|
if (!bbox) return [];
|
|
11229
11236
|
return [
|
|
11230
11237
|
{
|
|
11231
|
-
name:
|
|
11238
|
+
name: path7 || "shape",
|
|
11232
11239
|
shape: part,
|
|
11233
11240
|
bbox,
|
|
11234
11241
|
bodyCount: bodyCount(part)
|
|
@@ -11238,7 +11245,7 @@ function partShapeLeaves(part, path5 = "") {
|
|
|
11238
11245
|
const leaves = [];
|
|
11239
11246
|
part.children.forEach((child, index) => {
|
|
11240
11247
|
if (child instanceof Shape || child instanceof ShapeGroup) {
|
|
11241
|
-
leaves.push(...partShapeLeaves(child, childPath(
|
|
11248
|
+
leaves.push(...partShapeLeaves(child, childPath(path7, part.childName(index), index)));
|
|
11242
11249
|
}
|
|
11243
11250
|
});
|
|
11244
11251
|
return leaves;
|
|
@@ -14061,10 +14068,10 @@ function roundVec2(point) {
|
|
|
14061
14068
|
function roundVec3(point) {
|
|
14062
14069
|
return [round2(point[0]), round2(point[1]), round2(point[2])];
|
|
14063
14070
|
}
|
|
14064
|
-
function fingerprint(
|
|
14071
|
+
function fingerprint(path7) {
|
|
14065
14072
|
try {
|
|
14066
|
-
if (!statSync6(
|
|
14067
|
-
const hash = createHash("sha256").update(readFileSync8(
|
|
14073
|
+
if (!statSync6(path7).isFile()) return null;
|
|
14074
|
+
const hash = createHash("sha256").update(readFileSync8(path7)).digest("hex");
|
|
14068
14075
|
return `sha256:${hash}`;
|
|
14069
14076
|
} catch {
|
|
14070
14077
|
return null;
|
|
@@ -14473,8 +14480,8 @@ function printSectionSummary(result) {
|
|
|
14473
14480
|
console.log("");
|
|
14474
14481
|
console.log(`Replay: forgecad inspect replay ${result.artifacts.resultJson} --source <candidate>`);
|
|
14475
14482
|
}
|
|
14476
|
-
function readInspectionResult(
|
|
14477
|
-
const resolved = resolve11(
|
|
14483
|
+
function readInspectionResult(path7) {
|
|
14484
|
+
const resolved = resolve11(path7);
|
|
14478
14485
|
if (!existsSync7(resolved)) throw new Error(`Inspection result not found: ${resolved}`);
|
|
14479
14486
|
const parsed = JSON.parse(readFileSync8(resolved, "utf-8"));
|
|
14480
14487
|
if (parsed.kind !== "forgecad.inspect.section.result" || parsed.replaySpec?.tool !== "section") {
|
|
@@ -14791,20 +14798,20 @@ function selectRegion(regions, seed, operation) {
|
|
|
14791
14798
|
operationCompatible: false
|
|
14792
14799
|
};
|
|
14793
14800
|
}
|
|
14794
|
-
function collectProfileUses(plan,
|
|
14801
|
+
function collectProfileUses(plan, path7 = "$") {
|
|
14795
14802
|
switch (plan.kind) {
|
|
14796
14803
|
case "extrude":
|
|
14797
|
-
return [{ operation: "extrude", profile: plan.profile, path: `${
|
|
14804
|
+
return [{ operation: "extrude", profile: plan.profile, path: `${path7}.profile` }];
|
|
14798
14805
|
case "cut":
|
|
14799
|
-
return [...collectProfileUses(plan.base, `${
|
|
14806
|
+
return [...collectProfileUses(plan.base, `${path7}.base`), { operation: "cut", profile: plan.profile, path: `${path7}.profile` }];
|
|
14800
14807
|
case "revolve":
|
|
14801
|
-
return [{ operation: "revolve", profile: plan.profile, path: `${
|
|
14808
|
+
return [{ operation: "revolve", profile: plan.profile, path: `${path7}.profile` }];
|
|
14802
14809
|
case "queryOwner":
|
|
14803
|
-
return collectProfileUses(plan.base, `${
|
|
14810
|
+
return collectProfileUses(plan.base, `${path7}.base`);
|
|
14804
14811
|
case "transform":
|
|
14805
|
-
return collectProfileUses(plan.base, `${
|
|
14812
|
+
return collectProfileUses(plan.base, `${path7}.base`);
|
|
14806
14813
|
case "boolean":
|
|
14807
|
-
return plan.shapes.flatMap((shape, index) => collectProfileUses(shape, `${
|
|
14814
|
+
return plan.shapes.flatMap((shape, index) => collectProfileUses(shape, `${path7}.shapes[${index}]`));
|
|
14808
14815
|
case "shell":
|
|
14809
14816
|
case "hole":
|
|
14810
14817
|
case "trimByPlane":
|
|
@@ -14817,32 +14824,32 @@ function collectProfileUses(plan, path5 = "$") {
|
|
|
14817
14824
|
case "surfaceExtend":
|
|
14818
14825
|
case "surfaceThicken":
|
|
14819
14826
|
case "surfaceSolid":
|
|
14820
|
-
return collectProfileUses(plan.base, `${
|
|
14827
|
+
return collectProfileUses(plan.base, `${path7}.base`);
|
|
14821
14828
|
case "surfaceSew":
|
|
14822
|
-
return plan.shapes.flatMap((shape, index) => collectProfileUses(shape, `${
|
|
14829
|
+
return plan.shapes.flatMap((shape, index) => collectProfileUses(shape, `${path7}.shapes[${index}]`));
|
|
14823
14830
|
default:
|
|
14824
14831
|
return [];
|
|
14825
14832
|
}
|
|
14826
14833
|
}
|
|
14827
|
-
function summarizeProfilePlan(plan,
|
|
14834
|
+
function summarizeProfilePlan(plan, path7) {
|
|
14828
14835
|
switch (plan.kind) {
|
|
14829
14836
|
case "boolean":
|
|
14830
14837
|
return {
|
|
14831
|
-
path:
|
|
14838
|
+
path: path7,
|
|
14832
14839
|
kind: plan.kind,
|
|
14833
14840
|
op: plan.op,
|
|
14834
|
-
children: plan.profiles.map((profile, index) => summarizeProfilePlan(profile, `${
|
|
14841
|
+
children: plan.profiles.map((profile, index) => summarizeProfilePlan(profile, `${path7}.profiles[${index}]`))
|
|
14835
14842
|
};
|
|
14836
14843
|
case "offset":
|
|
14837
|
-
return { path:
|
|
14844
|
+
return { path: path7, kind: plan.kind, children: [summarizeProfilePlan(plan.base, `${path7}.base`)] };
|
|
14838
14845
|
case "project":
|
|
14839
14846
|
return {
|
|
14840
|
-
path:
|
|
14847
|
+
path: path7,
|
|
14841
14848
|
kind: plan.kind,
|
|
14842
|
-
children: plan.replayProfile ? [summarizeProfilePlan(plan.replayProfile, `${
|
|
14849
|
+
children: plan.replayProfile ? [summarizeProfilePlan(plan.replayProfile, `${path7}.replayProfile`)] : []
|
|
14843
14850
|
};
|
|
14844
14851
|
default:
|
|
14845
|
-
return { path:
|
|
14852
|
+
return { path: path7, kind: plan.kind, children: [] };
|
|
14846
14853
|
}
|
|
14847
14854
|
}
|
|
14848
14855
|
function readSketchBounds(sketch, diagnostics) {
|
|
@@ -17315,12 +17322,12 @@ var ESTABLISHED_LOWERCASE_PUBLIC_API_GLOBALS = /* @__PURE__ */ new Set([
|
|
|
17315
17322
|
"require"
|
|
17316
17323
|
]);
|
|
17317
17324
|
function readText(relativePath) {
|
|
17318
|
-
const
|
|
17319
|
-
return readFileSync9(
|
|
17325
|
+
const path7 = resolvePackagePath(import.meta.url, relativePath);
|
|
17326
|
+
return readFileSync9(path7, "utf8");
|
|
17320
17327
|
}
|
|
17321
17328
|
function readSource(relativePath) {
|
|
17322
|
-
const
|
|
17323
|
-
return ts.createSourceFile(
|
|
17329
|
+
const path7 = resolvePackagePath(import.meta.url, relativePath);
|
|
17330
|
+
return ts.createSourceFile(path7, readText(relativePath), ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
17324
17331
|
}
|
|
17325
17332
|
function propertyNameText(name) {
|
|
17326
17333
|
if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) return name.text;
|
|
@@ -17595,8 +17602,8 @@ function parseArgs4(argv) {
|
|
|
17595
17602
|
requireScriptInputPaths(inputPaths);
|
|
17596
17603
|
return { inputPaths, json, compact, noFailExit, paramOverrides, jointOverrides };
|
|
17597
17604
|
}
|
|
17598
|
-
function finding2(code, message,
|
|
17599
|
-
return { level: "error", code, message, path:
|
|
17605
|
+
function finding2(code, message, path7) {
|
|
17606
|
+
return { level: "error", code, message, path: path7 };
|
|
17600
17607
|
}
|
|
17601
17608
|
function failedReport(source, modelName, errorMessage2, code = "SIM.CHECK.ERROR") {
|
|
17602
17609
|
const errors = [finding2(code, errorMessage2)];
|
|
@@ -17616,8 +17623,8 @@ function resolveSimulationModel(result, jointOverrides) {
|
|
|
17616
17623
|
return collectSimulationModel(assembly2.describe(), { state: jointOverrides });
|
|
17617
17624
|
}
|
|
17618
17625
|
function renderFinding(finding3) {
|
|
17619
|
-
const
|
|
17620
|
-
return ` - [${finding3.code}] ${finding3.message}${
|
|
17626
|
+
const path7 = finding3.path ? ` (${finding3.path})` : "";
|
|
17627
|
+
return ` - [${finding3.code}] ${finding3.message}${path7}`;
|
|
17621
17628
|
}
|
|
17622
17629
|
function printHuman3(report) {
|
|
17623
17630
|
if (report.ok) {
|
|
@@ -18076,8 +18083,8 @@ function renderFeedbackText(report) {
|
|
|
18076
18083
|
for (const finding3 of report.findings) {
|
|
18077
18084
|
const glyph = finding3.level === "error" ? "\u2717" : finding3.level === "warning" ? "\u26A0" : "\xB7";
|
|
18078
18085
|
const suggest = finding3.suggest ? ` \u2192 ${finding3.suggest}` : "";
|
|
18079
|
-
const
|
|
18080
|
-
lines.push(` ${glyph} [${finding3.code}] ${finding3.message}${
|
|
18086
|
+
const path7 = finding3.path ? ` (${finding3.path})` : "";
|
|
18087
|
+
lines.push(` ${glyph} [${finding3.code}] ${finding3.message}${path7}${suggest}`);
|
|
18081
18088
|
}
|
|
18082
18089
|
if (report.trust.assumptions.length > 0) {
|
|
18083
18090
|
lines.push(` assumptions: ${report.trust.assumptions.join("; ")}`);
|
|
@@ -18882,10 +18889,10 @@ function commandPatternMatches(pattern, commandPath) {
|
|
|
18882
18889
|
if (pattern.length === 0 || pattern.length > commandPath.length) return false;
|
|
18883
18890
|
return pattern.every((part, index) => part === commandPath[index]);
|
|
18884
18891
|
}
|
|
18885
|
-
function readForgeCadCliConfig(
|
|
18886
|
-
if (!existsSync8(
|
|
18887
|
-
const parsed = JSON.parse(readFileSync11(
|
|
18888
|
-
return { config: parsed, path:
|
|
18892
|
+
function readForgeCadCliConfig(path7 = defaultConfigPath()) {
|
|
18893
|
+
if (!existsSync8(path7)) return null;
|
|
18894
|
+
const parsed = JSON.parse(readFileSync11(path7, "utf-8"));
|
|
18895
|
+
return { config: parsed, path: path7 };
|
|
18889
18896
|
}
|
|
18890
18897
|
function disabledCommandMatch(commandPath, loaded = readForgeCadCliConfig()) {
|
|
18891
18898
|
const command = commandPath.join(" ");
|
|
@@ -19288,20 +19295,20 @@ async function buildBrepBlob(objects) {
|
|
|
19288
19295
|
}
|
|
19289
19296
|
topoShape = compound;
|
|
19290
19297
|
}
|
|
19291
|
-
const
|
|
19298
|
+
const path7 = "/tmp/forgecad-export.brep";
|
|
19292
19299
|
const pr = new oc.Message_ProgressRange_1();
|
|
19293
|
-
const writeSuccess = oc.BRepTools.Write_3(topoShape,
|
|
19300
|
+
const writeSuccess = oc.BRepTools.Write_3(topoShape, path7, pr);
|
|
19294
19301
|
if (!writeSuccess) {
|
|
19295
19302
|
throw new Error("BREP export: BRepTools.Write_3 returned failure.");
|
|
19296
19303
|
}
|
|
19297
19304
|
let data;
|
|
19298
19305
|
try {
|
|
19299
|
-
data = oc.FS.readFile(
|
|
19306
|
+
data = oc.FS.readFile(path7);
|
|
19300
19307
|
} catch (err) {
|
|
19301
19308
|
throw new Error(`BREP export: failed to read written file from virtual FS \u2014 ${err.message || err}`);
|
|
19302
19309
|
}
|
|
19303
19310
|
try {
|
|
19304
|
-
oc.FS.unlink(
|
|
19311
|
+
oc.FS.unlink(path7);
|
|
19305
19312
|
} catch {
|
|
19306
19313
|
}
|
|
19307
19314
|
return new Blob([data.buffer], { type: "application/octet-stream" });
|
|
@@ -19980,9 +19987,9 @@ function readValue4(argv, idx, flag) {
|
|
|
19980
19987
|
}
|
|
19981
19988
|
return next;
|
|
19982
19989
|
}
|
|
19983
|
-
function inferFormatFromPath(
|
|
19984
|
-
if (!
|
|
19985
|
-
const ext = extname4(
|
|
19990
|
+
function inferFormatFromPath(path7) {
|
|
19991
|
+
if (!path7) return null;
|
|
19992
|
+
const ext = extname4(path7).toLowerCase();
|
|
19986
19993
|
if (ext === ".gif") return "gif";
|
|
19987
19994
|
if (ext === ".mp4") return "mp4";
|
|
19988
19995
|
return null;
|
|
@@ -21395,9 +21402,9 @@ function normalizeFeaArtifactDigest(value, label) {
|
|
|
21395
21402
|
function normalizeFeaArtifactDigestMap(value, label) {
|
|
21396
21403
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object`);
|
|
21397
21404
|
const map = {};
|
|
21398
|
-
for (const [
|
|
21399
|
-
if (!
|
|
21400
|
-
map[
|
|
21405
|
+
for (const [path7, digest] of Object.entries(value)) {
|
|
21406
|
+
if (!path7.trim()) throw new Error(`${label} cannot contain an empty path`);
|
|
21407
|
+
map[path7] = normalizeFeaArtifactDigest(digest, `${label}.${path7}`);
|
|
21401
21408
|
}
|
|
21402
21409
|
return map;
|
|
21403
21410
|
}
|
|
@@ -24127,8 +24134,8 @@ function parseGmshMsh41SurfaceMesh(text, surfaceEntityIds) {
|
|
|
24127
24134
|
if (triangles.length === 0) throw new Error("Gmsh MSH contains no exterior surface triangles for FEA deformed rendering.");
|
|
24128
24135
|
return { nodes, triangles, surfaceElements };
|
|
24129
24136
|
}
|
|
24130
|
-
function readJson(packageDir,
|
|
24131
|
-
return requireRecord(JSON.parse(readFileSync17(join9(packageDir,
|
|
24137
|
+
function readJson(packageDir, path7) {
|
|
24138
|
+
return requireRecord(JSON.parse(readFileSync17(join9(packageDir, path7), "utf-8")), path7);
|
|
24132
24139
|
}
|
|
24133
24140
|
function deformedPosition(base, displacement, scaleFactor) {
|
|
24134
24141
|
return add2(base, scale2(displacement, scaleFactor));
|
|
@@ -24235,8 +24242,8 @@ function positiveFiniteNumber(value, label) {
|
|
|
24235
24242
|
}
|
|
24236
24243
|
return value;
|
|
24237
24244
|
}
|
|
24238
|
-
function readPackageJson(packageDir,
|
|
24239
|
-
return JSON.parse(readFileSync18(join10(packageDir,
|
|
24245
|
+
function readPackageJson(packageDir, path7) {
|
|
24246
|
+
return JSON.parse(readFileSync18(join10(packageDir, path7), "utf-8"));
|
|
24240
24247
|
}
|
|
24241
24248
|
function packageYieldStrengthMpa(packageDir) {
|
|
24242
24249
|
const manifest = readPackageJson(packageDir, "fea-manifest.json");
|
|
@@ -25599,9 +25606,9 @@ function fileStemForObject(name) {
|
|
|
25599
25606
|
function defaultOutputPath4(scriptPath, format) {
|
|
25600
25607
|
return replaceCliInputExtension(scriptPath, format === "json" ? ".implicit.json" : ".implicit-webgpu");
|
|
25601
25608
|
}
|
|
25602
|
-
function writeJsonFile(
|
|
25603
|
-
mkdirSync6(dirname6(
|
|
25604
|
-
writeFileSync11(
|
|
25609
|
+
function writeJsonFile(path7, value) {
|
|
25610
|
+
mkdirSync6(dirname6(path7), { recursive: true });
|
|
25611
|
+
writeFileSync11(path7, `${JSON.stringify(value, null, 2)}
|
|
25605
25612
|
`, "utf-8");
|
|
25606
25613
|
}
|
|
25607
25614
|
async function runImplicitExportCli(argv = process.argv.slice(2)) {
|
|
@@ -29695,19 +29702,19 @@ function requireMetricValue(report, key) {
|
|
|
29695
29702
|
if (value === null) throw new Error(`Solved structural FEA feedback is missing finite metric ${key}`);
|
|
29696
29703
|
return value;
|
|
29697
29704
|
}
|
|
29698
|
-
function requireDigest(map,
|
|
29699
|
-
const digest = map[
|
|
29700
|
-
if (!digest) throw new Error(`fea-manifest.json artifactDigests is missing ${label} (${
|
|
29705
|
+
function requireDigest(map, path7, label) {
|
|
29706
|
+
const digest = map[path7];
|
|
29707
|
+
if (!digest) throw new Error(`fea-manifest.json artifactDigests is missing ${label} (${path7}).`);
|
|
29701
29708
|
return digest;
|
|
29702
29709
|
}
|
|
29703
|
-
function requireActualDigest(map,
|
|
29704
|
-
const digest = map[
|
|
29705
|
-
if (!digest) throw new Error(`actual package artifact digest is missing ${label} (${
|
|
29710
|
+
function requireActualDigest(map, path7, label) {
|
|
29711
|
+
const digest = map[path7];
|
|
29712
|
+
if (!digest) throw new Error(`actual package artifact digest is missing ${label} (${path7}).`);
|
|
29706
29713
|
return digest;
|
|
29707
29714
|
}
|
|
29708
|
-
function requirePackageDigestMatch(expected, actual,
|
|
29709
|
-
const expectedDigest = requireDigest(expected,
|
|
29710
|
-
requireMatchingFeaArtifactDigest(requireActualDigest(actual,
|
|
29715
|
+
function requirePackageDigestMatch(expected, actual, path7, label) {
|
|
29716
|
+
const expectedDigest = requireDigest(expected, path7, label);
|
|
29717
|
+
requireMatchingFeaArtifactDigest(requireActualDigest(actual, path7, label), expectedDigest, label);
|
|
29711
29718
|
return expectedDigest;
|
|
29712
29719
|
}
|
|
29713
29720
|
function resolveStructuralStressPackageDigests(manifestArtifactDigests, actualArtifactDigests, paths) {
|
|
@@ -29726,8 +29733,8 @@ function resolveStructuralStressPackageDigests(manifestArtifactDigests, actualAr
|
|
|
29726
29733
|
} : {}
|
|
29727
29734
|
};
|
|
29728
29735
|
}
|
|
29729
|
-
function requireArtifactDigestForPath(digests,
|
|
29730
|
-
return requirePackageDigestMatch(digests.expected, digests.actual,
|
|
29736
|
+
function requireArtifactDigestForPath(digests, path7, label) {
|
|
29737
|
+
return requirePackageDigestMatch(digests.expected, digests.actual, path7, label);
|
|
29731
29738
|
}
|
|
29732
29739
|
function requireFeedbackMetricMatches(report, key, value) {
|
|
29733
29740
|
const feedbackValue = requireMetricValue(report, key);
|
|
@@ -29977,15 +29984,15 @@ function failedReport4(source, message, code, suggest) {
|
|
|
29977
29984
|
timeMs: 0
|
|
29978
29985
|
};
|
|
29979
29986
|
}
|
|
29980
|
-
function readPackageJson2(packageDir,
|
|
29981
|
-
return JSON.parse(readFileSync22(join16(packageDir,
|
|
29987
|
+
function readPackageJson2(packageDir, path7) {
|
|
29988
|
+
return JSON.parse(readFileSync22(join16(packageDir, path7), "utf-8"));
|
|
29982
29989
|
}
|
|
29983
29990
|
function digestPackageArtifacts(packageDir, manifest) {
|
|
29984
29991
|
const declared = manifest.artifactDigests;
|
|
29985
29992
|
if (!declared || typeof declared !== "object" || Array.isArray(declared)) return {};
|
|
29986
29993
|
const digests = {};
|
|
29987
|
-
for (const
|
|
29988
|
-
digests[
|
|
29994
|
+
for (const path7 of Object.keys(declared)) {
|
|
29995
|
+
digests[path7] = digestFeaArtifactBytes(readFileSync22(join16(packageDir, path7)));
|
|
29989
29996
|
}
|
|
29990
29997
|
return digests;
|
|
29991
29998
|
}
|
|
@@ -31954,6 +31961,7 @@ async function exportSketchPdfInput(scriptPath, explicitOutputPath, paramOverrid
|
|
|
31954
31961
|
import { cpSync, existsSync as existsSync17, mkdirSync as mkdirSync9, readdirSync as readdirSync6, readFileSync as readFileSync24, rmSync as rmSync4, writeFileSync as writeFileSync16 } from "fs";
|
|
31955
31962
|
import { homedir as homedir6 } from "os";
|
|
31956
31963
|
import { extname as extname12, join as join17, relative as relative5, resolve as resolve40 } from "path";
|
|
31964
|
+
import { createInterface } from "readline";
|
|
31957
31965
|
var INSTALL_TARGETS = {
|
|
31958
31966
|
agents: { label: "agent-compatible", root: join17(homedir6(), ".agents", "skills") },
|
|
31959
31967
|
claude: { label: "Claude Code", root: join17(homedir6(), ".claude", "skills") },
|
|
@@ -31962,27 +31970,6 @@ var INSTALL_TARGETS = {
|
|
|
31962
31970
|
};
|
|
31963
31971
|
var ALL_INSTALL_TARGETS = Object.keys(INSTALL_TARGETS);
|
|
31964
31972
|
var DEFAULT_INSTALL_TARGET = "agents";
|
|
31965
|
-
var RETIRED_COMPANION_SKILL_NAMES = [
|
|
31966
|
-
"forgecad-3d-reconstruction",
|
|
31967
|
-
"forgecad-assembly-contract",
|
|
31968
|
-
"forgecad-benchmark-reconstruction",
|
|
31969
|
-
"forgecad-blockout",
|
|
31970
|
-
"forgecad-blockout-model",
|
|
31971
|
-
"forgecad-component-model",
|
|
31972
|
-
"forgecad-high-level-spec",
|
|
31973
|
-
"forgecad-image-replicator",
|
|
31974
|
-
"forgecad-lld",
|
|
31975
|
-
"forgecad-make-a-model",
|
|
31976
|
-
"forgecad-model-grader",
|
|
31977
|
-
"forgecad-mujoco-verify",
|
|
31978
|
-
"forgecad-prepare-prompt",
|
|
31979
|
-
"forgecad-project",
|
|
31980
|
-
"forgecad-reconstruction-benchmark",
|
|
31981
|
-
"forgecad-render-inspect",
|
|
31982
|
-
"forgecad-spec-by-walking-through-it",
|
|
31983
|
-
"forgecad-visual-prompt",
|
|
31984
|
-
"forgecad-visual-spec"
|
|
31985
|
-
];
|
|
31986
31973
|
function installUsage() {
|
|
31987
31974
|
return [
|
|
31988
31975
|
"Usage: forgecad skill install [--target agents|claude|codex|opencode|all] [--core-only]",
|
|
@@ -32073,7 +32060,39 @@ If you are running from a source checkout, run: npm run build:skill:forgecad`
|
|
|
32073
32060
|
console.log(`ForgeCAD skill installed to ${dest} [standard model authoring]`);
|
|
32074
32061
|
return dest;
|
|
32075
32062
|
}
|
|
32076
|
-
function
|
|
32063
|
+
function confirm(question) {
|
|
32064
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
32065
|
+
return new Promise((resolve54) => {
|
|
32066
|
+
rl.question(`${question} [y/N] `, (answer) => {
|
|
32067
|
+
rl.close();
|
|
32068
|
+
resolve54(/^(y|yes)$/i.test(answer.trim()));
|
|
32069
|
+
});
|
|
32070
|
+
});
|
|
32071
|
+
}
|
|
32072
|
+
function discoverCompanionSkillNames(srcLibrary) {
|
|
32073
|
+
return readdirSync6(srcLibrary, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync17(join17(srcLibrary, entry.name, "SKILL.md"))).map((entry) => entry.name).sort();
|
|
32074
|
+
}
|
|
32075
|
+
function findStaleForgeCadCompanionSkillNames(targetRoot, currentSkillNames) {
|
|
32076
|
+
if (!existsSync17(targetRoot)) return [];
|
|
32077
|
+
return readdirSync6(targetRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => name.startsWith("forgecad-")).filter((name) => !currentSkillNames.has(name)).filter((name) => existsSync17(join17(targetRoot, name, "SKILL.md"))).sort();
|
|
32078
|
+
}
|
|
32079
|
+
async function removeConfirmedStaleCompanionSkills(targetRoot, currentSkillNames) {
|
|
32080
|
+
const staleNames = findStaleForgeCadCompanionSkillNames(targetRoot, currentSkillNames);
|
|
32081
|
+
if (staleNames.length === 0) return;
|
|
32082
|
+
console.warn(`Found ForgeCAD-named skill directories that are not in this package at ${targetRoot}:`);
|
|
32083
|
+
for (const name of staleNames) console.warn(` - ${name}`);
|
|
32084
|
+
if (!process.stdin.isTTY || !process.stderr.isTTY) {
|
|
32085
|
+
console.warn("Skipping stale skill cleanup because this shell is not interactive. Delete them manually or rerun in a terminal.");
|
|
32086
|
+
return;
|
|
32087
|
+
}
|
|
32088
|
+
if (!await confirm("Remove these stale ForgeCAD companion skill directories?")) {
|
|
32089
|
+
console.log("Skipped stale ForgeCAD companion skill cleanup.");
|
|
32090
|
+
return;
|
|
32091
|
+
}
|
|
32092
|
+
for (const name of staleNames) rmSync4(join17(targetRoot, name), { recursive: true, force: true });
|
|
32093
|
+
console.log(`Removed ${staleNames.length} stale ForgeCAD companion skill${staleNames.length === 1 ? "" : "s"}.`);
|
|
32094
|
+
}
|
|
32095
|
+
async function installCompanionLibrary(targetRoot) {
|
|
32077
32096
|
const srcLibrary = resolvePackagePath(import.meta.url, "dist-skill", "library");
|
|
32078
32097
|
if (!existsSync17(srcLibrary)) {
|
|
32079
32098
|
throw new Error(
|
|
@@ -32082,17 +32101,15 @@ If you are running from a source checkout, run: npm run build:skill:forgecad`
|
|
|
32082
32101
|
);
|
|
32083
32102
|
}
|
|
32084
32103
|
mkdirSync9(targetRoot, { recursive: true });
|
|
32085
|
-
|
|
32086
|
-
|
|
32087
|
-
}
|
|
32104
|
+
const currentSkillNames = discoverCompanionSkillNames(srcLibrary);
|
|
32105
|
+
await removeConfirmedStaleCompanionSkills(targetRoot, new Set(currentSkillNames));
|
|
32088
32106
|
const installed = [];
|
|
32089
|
-
for (const
|
|
32090
|
-
|
|
32091
|
-
const
|
|
32092
|
-
const dest = join17(targetRoot, entry.name);
|
|
32107
|
+
for (const name of currentSkillNames) {
|
|
32108
|
+
const src = join17(srcLibrary, name);
|
|
32109
|
+
const dest = join17(targetRoot, name);
|
|
32093
32110
|
rmSync4(dest, { recursive: true, force: true });
|
|
32094
32111
|
cpSync(src, dest, { recursive: true });
|
|
32095
|
-
installed.push(
|
|
32112
|
+
installed.push(name);
|
|
32096
32113
|
}
|
|
32097
32114
|
if (installed.length === 0) {
|
|
32098
32115
|
throw new Error(`No companion skills found in ${srcLibrary}`);
|
|
@@ -32107,7 +32124,7 @@ async function runSkillInstallCli(argv = []) {
|
|
|
32107
32124
|
const config = INSTALL_TARGETS[target];
|
|
32108
32125
|
console.log(`Installing ForgeCAD skills for ${config.label} at ${config.root}`);
|
|
32109
32126
|
installCoreSkill(config.root);
|
|
32110
|
-
if (options.includeLibrary) installCompanionLibrary(config.root);
|
|
32127
|
+
if (options.includeLibrary) await installCompanionLibrary(config.root);
|
|
32111
32128
|
}
|
|
32112
32129
|
console.log(`Reload your agent (Claude Code, Codex, OpenCode, \u2026) to activate.`);
|
|
32113
32130
|
}
|
|
@@ -32129,11 +32146,11 @@ If you are running from a source checkout, run: npm run build:skill:forgecad`
|
|
|
32129
32146
|
console.log(`ForgeCAD context written to ${dest}`);
|
|
32130
32147
|
console.log(`Paste the contents into any AI chat UI (Claude.ai, ChatGPT, Gemini, \u2026) to get full ForgeCAD API knowledge.`);
|
|
32131
32148
|
}
|
|
32132
|
-
function normalizeRelativePath(
|
|
32133
|
-
return
|
|
32149
|
+
function normalizeRelativePath(path7) {
|
|
32150
|
+
return path7.replace(/\\/g, "/");
|
|
32134
32151
|
}
|
|
32135
|
-
function languageForPath(
|
|
32136
|
-
const ext = extname12(
|
|
32152
|
+
function languageForPath(path7) {
|
|
32153
|
+
const ext = extname12(path7).toLowerCase();
|
|
32137
32154
|
if (ext === ".js" || ext === ".mjs" || ext === ".cjs") return "js";
|
|
32138
32155
|
if (ext === ".ts" || ext === ".tsx") return "ts";
|
|
32139
32156
|
if (ext === ".json") return "json";
|
|
@@ -32165,8 +32182,8 @@ function findSkillFiles(root) {
|
|
|
32165
32182
|
return a.label.localeCompare(b.label);
|
|
32166
32183
|
});
|
|
32167
32184
|
}
|
|
32168
|
-
function renderFlattenedFile(label,
|
|
32169
|
-
const raw = readFileSync24(
|
|
32185
|
+
function renderFlattenedFile(label, path7, isFirst) {
|
|
32186
|
+
const raw = readFileSync24(path7, "utf-8").trimEnd().replaceAll("{{SKILL_DIR}}/", "").replaceAll("{{SKILL_DIR}}", ".");
|
|
32170
32187
|
if (isFirst) return raw;
|
|
32171
32188
|
if (label.endsWith(".md")) {
|
|
32172
32189
|
return [`## File: \`${label}\``, "", raw].join("\n");
|
|
@@ -32239,9 +32256,9 @@ import { resolve as resolve41 } from "path";
|
|
|
32239
32256
|
|
|
32240
32257
|
// cli/forge-studio-server.ts
|
|
32241
32258
|
import chokidar from "chokidar";
|
|
32242
|
-
import
|
|
32259
|
+
import fs3 from "fs";
|
|
32243
32260
|
import http from "http";
|
|
32244
|
-
import
|
|
32261
|
+
import path4 from "path";
|
|
32245
32262
|
|
|
32246
32263
|
// server/importAnalysis.ts
|
|
32247
32264
|
var FORGE_IMPORT_RE2 = /\b(?:importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.svgSketch|Import\.dxfSketch)\s*\(\s*(?:"([^"]+)"|'([^']+)')/g;
|
|
@@ -32269,19 +32286,19 @@ function extractImports2(code) {
|
|
|
32269
32286
|
let m;
|
|
32270
32287
|
const forgeRe = new RegExp(FORGE_IMPORT_RE2.source, FORGE_IMPORT_RE2.flags);
|
|
32271
32288
|
while ((m = forgeRe.exec(code)) !== null) {
|
|
32272
|
-
const
|
|
32289
|
+
const path7 = m[1] ?? m[2];
|
|
32273
32290
|
const fn = m[0].match(/\b(importMesh|importStep|importSvgSketch|Import\.mesh|Import\.step|Import\.svgSketch|Import\.dxfSketch)/)?.[1];
|
|
32274
|
-
add3(
|
|
32291
|
+
add3(path7, kindMap[fn] ?? "forgeMesh");
|
|
32275
32292
|
}
|
|
32276
32293
|
const reqRe = new RegExp(REQUIRE_RE2.source, REQUIRE_RE2.flags);
|
|
32277
32294
|
while ((m = reqRe.exec(code)) !== null) {
|
|
32278
|
-
const
|
|
32279
|
-
add3(
|
|
32295
|
+
const path7 = m[1] ?? m[2];
|
|
32296
|
+
add3(path7, "require");
|
|
32280
32297
|
}
|
|
32281
32298
|
const esRe = new RegExp(ES_IMPORT_RE2.source, ES_IMPORT_RE2.flags);
|
|
32282
32299
|
while ((m = esRe.exec(code)) !== null) {
|
|
32283
|
-
const
|
|
32284
|
-
add3(
|
|
32300
|
+
const path7 = m[1] ?? m[2];
|
|
32301
|
+
add3(path7, "esImport");
|
|
32285
32302
|
}
|
|
32286
32303
|
return refs;
|
|
32287
32304
|
}
|
|
@@ -32350,7 +32367,7 @@ import { join as join19 } from "path";
|
|
|
32350
32367
|
import { chmodSync as chmodSync2, existsSync as existsSync18, mkdirSync as mkdirSync10, readFileSync as readFileSync25, unlinkSync, writeFileSync as writeFileSync17 } from "fs";
|
|
32351
32368
|
import { homedir as homedir7 } from "os";
|
|
32352
32369
|
import { join as join18 } from "path";
|
|
32353
|
-
import { createInterface } from "readline";
|
|
32370
|
+
import { createInterface as createInterface2 } from "readline";
|
|
32354
32371
|
|
|
32355
32372
|
// cli/server-url.ts
|
|
32356
32373
|
import { execFile, execFileSync } from "child_process";
|
|
@@ -32396,14 +32413,14 @@ function normalizeServerUrl(raw, label = "Server URL") {
|
|
|
32396
32413
|
function isOfficialServerUrl(server) {
|
|
32397
32414
|
return normalizeServerUrl(server) === DEFAULT_SERVER;
|
|
32398
32415
|
}
|
|
32399
|
-
function requireRelativeApiPath(
|
|
32400
|
-
if (!
|
|
32416
|
+
function requireRelativeApiPath(path7) {
|
|
32417
|
+
if (!path7.startsWith("/") || path7.startsWith("//") || /^[a-z][a-z0-9+.-]*:/i.test(path7)) {
|
|
32401
32418
|
throw new Error("Authenticated ForgeCAD requests must use a relative API path.");
|
|
32402
32419
|
}
|
|
32403
|
-
return
|
|
32420
|
+
return path7;
|
|
32404
32421
|
}
|
|
32405
|
-
function serverUrlWithPath(server,
|
|
32406
|
-
return `${normalizeServerUrl(server)}${requireRelativeApiPath(
|
|
32422
|
+
function serverUrlWithPath(server, path7) {
|
|
32423
|
+
return `${normalizeServerUrl(server)}${requireRelativeApiPath(path7)}`;
|
|
32407
32424
|
}
|
|
32408
32425
|
function normalizeBrowserUrl(raw) {
|
|
32409
32426
|
const url = parseUrl(raw, "Browser URL");
|
|
@@ -32493,7 +32510,7 @@ function getEnvToken() {
|
|
|
32493
32510
|
return { token, server: normalizeServerUrl(process.env.FORGECAD_SERVER ?? DEFAULT_SERVER, "FORGECAD_SERVER") };
|
|
32494
32511
|
}
|
|
32495
32512
|
function prompt(question) {
|
|
32496
|
-
const rl =
|
|
32513
|
+
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
32497
32514
|
return new Promise((resolve54) => {
|
|
32498
32515
|
rl.question(question, (answer) => {
|
|
32499
32516
|
rl.close();
|
|
@@ -32684,8 +32701,8 @@ async function refreshTokens(auth) {
|
|
|
32684
32701
|
return null;
|
|
32685
32702
|
}
|
|
32686
32703
|
}
|
|
32687
|
-
async function authenticatedFetch(
|
|
32688
|
-
const apiPath = requireRelativeApiPath(
|
|
32704
|
+
async function authenticatedFetch(path7, init2) {
|
|
32705
|
+
const apiPath = requireRelativeApiPath(path7);
|
|
32689
32706
|
const envToken = getEnvToken();
|
|
32690
32707
|
if (envToken) {
|
|
32691
32708
|
const url = serverUrlWithPath(envToken.server, apiPath);
|
|
@@ -32816,6 +32833,19 @@ function requireManifest(projectDir) {
|
|
|
32816
32833
|
}
|
|
32817
32834
|
return m;
|
|
32818
32835
|
}
|
|
32836
|
+
function hasRemoteProject(manifest) {
|
|
32837
|
+
return typeof manifest.projectId === "string" && manifest.projectId.length > 0;
|
|
32838
|
+
}
|
|
32839
|
+
function requireRemoteManifest(projectDir, action = "use this command") {
|
|
32840
|
+
const manifest = requireManifest(projectDir);
|
|
32841
|
+
if (!hasRemoteProject(manifest)) {
|
|
32842
|
+
throw new Error(
|
|
32843
|
+
`This ForgeCAD project has not been created on the server yet.
|
|
32844
|
+
Run \`forgecad project push\` to create the hosted project, then ${action}.`
|
|
32845
|
+
);
|
|
32846
|
+
}
|
|
32847
|
+
return manifest;
|
|
32848
|
+
}
|
|
32819
32849
|
function contentHash(content) {
|
|
32820
32850
|
return createHash2("sha256").update(content, "utf-8").digest("hex").slice(0, 16);
|
|
32821
32851
|
}
|
|
@@ -32839,14 +32869,14 @@ function scanLocalFiles(projectDir) {
|
|
|
32839
32869
|
return files;
|
|
32840
32870
|
}
|
|
32841
32871
|
function remoteFilesFromInit(entries) {
|
|
32842
|
-
return Object.entries(entries).map(([
|
|
32843
|
-
path:
|
|
32872
|
+
return Object.entries(entries).map(([path7, content]) => ({
|
|
32873
|
+
path: path7,
|
|
32844
32874
|
content,
|
|
32845
32875
|
hash: contentHash(content)
|
|
32846
32876
|
}));
|
|
32847
32877
|
}
|
|
32848
|
-
function projectPathLeaf(
|
|
32849
|
-
return
|
|
32878
|
+
function projectPathLeaf(path7) {
|
|
32879
|
+
return path7.split("/").pop() ?? path7;
|
|
32850
32880
|
}
|
|
32851
32881
|
function findProjectMoveCandidates(fromPath, remoteFile, untracked) {
|
|
32852
32882
|
const fromLeaf = projectPathLeaf(fromPath);
|
|
@@ -32875,17 +32905,17 @@ function diffFiles(local, remote) {
|
|
|
32875
32905
|
const remoteMap = new Map(remote.map((f2) => [f2.path, f2]));
|
|
32876
32906
|
const allPaths = /* @__PURE__ */ new Set([...localMap.keys(), ...remoteMap.keys()]);
|
|
32877
32907
|
const diffs = [];
|
|
32878
|
-
for (const
|
|
32879
|
-
const l = localMap.get(
|
|
32880
|
-
const r = remoteMap.get(
|
|
32908
|
+
for (const path7 of allPaths) {
|
|
32909
|
+
const l = localMap.get(path7);
|
|
32910
|
+
const r = remoteMap.get(path7);
|
|
32881
32911
|
if (l && !r) {
|
|
32882
|
-
diffs.push({ path:
|
|
32912
|
+
diffs.push({ path: path7, status: "added" });
|
|
32883
32913
|
} else if (!l && r) {
|
|
32884
|
-
diffs.push({ path:
|
|
32914
|
+
diffs.push({ path: path7, status: "deleted" });
|
|
32885
32915
|
} else if (l && r && l.hash !== r.hash) {
|
|
32886
|
-
diffs.push({ path:
|
|
32916
|
+
diffs.push({ path: path7, status: "modified" });
|
|
32887
32917
|
} else {
|
|
32888
|
-
diffs.push({ path:
|
|
32918
|
+
diffs.push({ path: path7, status: "unchanged" });
|
|
32889
32919
|
}
|
|
32890
32920
|
}
|
|
32891
32921
|
return diffs.sort((a, b) => a.path.localeCompare(b.path));
|
|
@@ -33086,7 +33116,7 @@ import { existsSync as existsSync20, readdirSync as readdirSync8, statSync as st
|
|
|
33086
33116
|
import { createServer as createServer2 } from "net";
|
|
33087
33117
|
import { join as join20 } from "path";
|
|
33088
33118
|
var startedComputeServer = null;
|
|
33089
|
-
var REQUIRED_ENGINE_VERSION = "
|
|
33119
|
+
var REQUIRED_ENGINE_VERSION = "backend-ir-node-api-0.1.0";
|
|
33090
33120
|
var REQUIRED_PLAN_KINDS = [
|
|
33091
33121
|
"box",
|
|
33092
33122
|
"cylinder",
|
|
@@ -33119,12 +33149,14 @@ async function isHealthy(computeUrl) {
|
|
|
33119
33149
|
const response = await fetch(`${computeUrl}/health`, { signal: AbortSignal.timeout(1e3) });
|
|
33120
33150
|
if (!response.ok) return false;
|
|
33121
33151
|
const body = await response.json().catch(() => null);
|
|
33122
|
-
if (body?.status !== "ok" || body?.
|
|
33152
|
+
if (body?.status !== "ok" || body?.engineVersion !== REQUIRED_ENGINE_VERSION) return false;
|
|
33153
|
+
if (body?.nativeOcctAvailable !== true && body?.sdfAvailable !== true) return false;
|
|
33123
33154
|
const capabilitiesResponse = await fetch(`${computeUrl}/capabilities`, { signal: AbortSignal.timeout(1e3) });
|
|
33124
33155
|
if (!capabilitiesResponse.ok) return false;
|
|
33125
33156
|
const capabilities = await capabilitiesResponse.json().catch(() => null);
|
|
33126
33157
|
const supportedKinds = capabilities?.nativeOcct?.supportedPlanKinds;
|
|
33127
|
-
|
|
33158
|
+
const supportedSdfKinds = capabilities?.sdf?.supportedPlanKinds;
|
|
33159
|
+
return Array.isArray(supportedKinds) && REQUIRED_PLAN_KINDS.every((kind) => supportedKinds.includes(kind)) && Array.isArray(supportedSdfKinds) && supportedSdfKinds.includes("sdf");
|
|
33128
33160
|
} catch {
|
|
33129
33161
|
return false;
|
|
33130
33162
|
}
|
|
@@ -33135,7 +33167,7 @@ async function waitForHealthy(computeUrl, child) {
|
|
|
33135
33167
|
if (child.exitCode != null) break;
|
|
33136
33168
|
await new Promise((resolve54) => setTimeout(resolve54, 50));
|
|
33137
33169
|
}
|
|
33138
|
-
throw new Error(`
|
|
33170
|
+
throw new Error(`Backend compute server did not become healthy at ${computeUrl}.`);
|
|
33139
33171
|
}
|
|
33140
33172
|
function closeChild(child) {
|
|
33141
33173
|
return new Promise((resolve54) => {
|
|
@@ -33150,13 +33182,13 @@ function closeChild(child) {
|
|
|
33150
33182
|
function hasLocalNativeComputeSource(packageRoot) {
|
|
33151
33183
|
return existsSync20(join20(packageRoot, "apps/backend/src/server.ts")) && existsSync20(join20(packageRoot, "scripts/build-native-occt.mjs"));
|
|
33152
33184
|
}
|
|
33153
|
-
function newestMtimeMs(
|
|
33154
|
-
if (!existsSync20(
|
|
33155
|
-
const stat = statSync11(
|
|
33185
|
+
function newestMtimeMs(path7) {
|
|
33186
|
+
if (!existsSync20(path7)) return 0;
|
|
33187
|
+
const stat = statSync11(path7);
|
|
33156
33188
|
if (!stat.isDirectory()) return stat.mtimeMs;
|
|
33157
33189
|
let newest = stat.mtimeMs;
|
|
33158
|
-
for (const entry of readdirSync8(
|
|
33159
|
-
newest = Math.max(newest, newestMtimeMs(join20(
|
|
33190
|
+
for (const entry of readdirSync8(path7, { withFileTypes: true })) {
|
|
33191
|
+
newest = Math.max(newest, newestMtimeMs(join20(path7, entry.name)));
|
|
33160
33192
|
}
|
|
33161
33193
|
return newest;
|
|
33162
33194
|
}
|
|
@@ -33192,7 +33224,7 @@ async function pickComputeUrl(preferred) {
|
|
|
33192
33224
|
} catch {
|
|
33193
33225
|
}
|
|
33194
33226
|
}
|
|
33195
|
-
throw new Error(`No local port available for
|
|
33227
|
+
throw new Error(`No local port available for backend compute near ${preferred.href}.`);
|
|
33196
33228
|
}
|
|
33197
33229
|
function startLocalNativeComputeServer(options) {
|
|
33198
33230
|
if (startedComputeServer) return startedComputeServer;
|
|
@@ -33227,7 +33259,7 @@ function startLocalNativeComputeServer(options) {
|
|
|
33227
33259
|
const selected = new URL(computeUrl);
|
|
33228
33260
|
const port = selected.port || (selected.protocol === "https:" ? "443" : "80");
|
|
33229
33261
|
const host = listenHostForUrl(selected);
|
|
33230
|
-
console.log(`[compute] Starting
|
|
33262
|
+
console.log(`[compute] Starting backend compute server at ${computeUrl}`);
|
|
33231
33263
|
const child = spawn3("npx", ["tsx", "apps/backend/src/server.ts"], {
|
|
33232
33264
|
cwd: options.packageRoot,
|
|
33233
33265
|
env: { ...process.env, FORGE_BACKEND_PORT: port, FORGE_BACKEND_HOST: host },
|
|
@@ -33528,6 +33560,279 @@ function updateLocalAgentContextLatestBundle(projectRoot, filePath, latestBundle
|
|
|
33528
33560
|
return next;
|
|
33529
33561
|
}
|
|
33530
33562
|
|
|
33563
|
+
// cli/manual-param-edits-storage.ts
|
|
33564
|
+
import fs2 from "fs";
|
|
33565
|
+
import path3 from "path";
|
|
33566
|
+
|
|
33567
|
+
// src/studio/manual-params/schema.ts
|
|
33568
|
+
var MANUAL_PARAM_EDITS_DRAFT_KIND = "forgecad.manualParamEditsDraft.v1";
|
|
33569
|
+
var MANUAL_PARAM_EDITS_KIND = "forgecad.manualParamEdits.v1";
|
|
33570
|
+
function isRecord2(value) {
|
|
33571
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33572
|
+
}
|
|
33573
|
+
function requireString4(value, label) {
|
|
33574
|
+
if (typeof value !== "string" || value.trim() === "") throw new Error(`Invalid manual edits ${label}`);
|
|
33575
|
+
return value.trim();
|
|
33576
|
+
}
|
|
33577
|
+
function optionalString3(value) {
|
|
33578
|
+
if (value == null) return void 0;
|
|
33579
|
+
if (typeof value !== "string") throw new Error("Invalid manual edits unit");
|
|
33580
|
+
const trimmed = value.trim();
|
|
33581
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
33582
|
+
}
|
|
33583
|
+
function requireFiniteNumber2(value, label) {
|
|
33584
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`Invalid manual edits ${label}`);
|
|
33585
|
+
return value;
|
|
33586
|
+
}
|
|
33587
|
+
function requireInteger(value, label) {
|
|
33588
|
+
const number = requireFiniteNumber2(value, label);
|
|
33589
|
+
if (!Number.isInteger(number)) throw new Error(`Invalid manual edits ${label}`);
|
|
33590
|
+
return number;
|
|
33591
|
+
}
|
|
33592
|
+
function parseFilePath(value) {
|
|
33593
|
+
const filePath = requireString4(value, "filePath").replace(/\\/g, "/");
|
|
33594
|
+
const parts = filePath.split("/");
|
|
33595
|
+
if (filePath.startsWith("/") || parts.some((part) => part === "" || part === "." || part === "..") || filePath.includes("\0")) {
|
|
33596
|
+
throw new Error("Invalid manual edits filePath");
|
|
33597
|
+
}
|
|
33598
|
+
return filePath;
|
|
33599
|
+
}
|
|
33600
|
+
function parseContinuity(value, label) {
|
|
33601
|
+
if (value === "G0" || value === "G1" || value === "G2") return value;
|
|
33602
|
+
throw new Error(`Invalid manual edits ${label}`);
|
|
33603
|
+
}
|
|
33604
|
+
function parsePlacementRuleMode(value, label) {
|
|
33605
|
+
if (value === "allow" || value === "warn" || value === "prevent") return value;
|
|
33606
|
+
throw new Error(`Invalid manual edits ${label}`);
|
|
33607
|
+
}
|
|
33608
|
+
function parseOverrides(value) {
|
|
33609
|
+
if (!isRecord2(value)) throw new Error("Invalid manual edits overrides");
|
|
33610
|
+
const overrides = {};
|
|
33611
|
+
for (const [key, rawValue] of Object.entries(value)) {
|
|
33612
|
+
if (typeof rawValue !== "number" && typeof rawValue !== "string") throw new Error(`Invalid manual edits overrides.${key}`);
|
|
33613
|
+
if (typeof rawValue === "number" && !Number.isFinite(rawValue)) throw new Error(`Invalid manual edits overrides.${key}`);
|
|
33614
|
+
overrides[key] = rawValue;
|
|
33615
|
+
}
|
|
33616
|
+
return Object.fromEntries(Object.entries(overrides).sort(([a], [b]) => a.localeCompare(b)));
|
|
33617
|
+
}
|
|
33618
|
+
function parsePathPoint(value, label) {
|
|
33619
|
+
if (!isRecord2(value)) throw new Error(`Invalid manual edits ${label}`);
|
|
33620
|
+
return {
|
|
33621
|
+
x: requireFiniteNumber2(value.x, `${label}.x`),
|
|
33622
|
+
y: requireFiniteNumber2(value.y, `${label}.y`)
|
|
33623
|
+
};
|
|
33624
|
+
}
|
|
33625
|
+
function parseSplinePoint(value, label) {
|
|
33626
|
+
const point = parsePathPoint(value, label);
|
|
33627
|
+
return { ...point, g: parseContinuity(isRecord2(value) ? value.g : void 0, `${label}.g`) };
|
|
33628
|
+
}
|
|
33629
|
+
function parsePlacementFootprint(value, label) {
|
|
33630
|
+
if (!isRecord2(value)) throw new Error(`Invalid manual edits ${label}`);
|
|
33631
|
+
const type = requireString4(value.type, `${label}.type`);
|
|
33632
|
+
if (type === "rect") {
|
|
33633
|
+
return {
|
|
33634
|
+
type,
|
|
33635
|
+
width: requireFiniteNumber2(value.width, `${label}.width`),
|
|
33636
|
+
height: requireFiniteNumber2(value.height, `${label}.height`)
|
|
33637
|
+
};
|
|
33638
|
+
}
|
|
33639
|
+
if (type === "circle") {
|
|
33640
|
+
return { type, radius: requireFiniteNumber2(value.radius, `${label}.radius`) };
|
|
33641
|
+
}
|
|
33642
|
+
throw new Error(`Invalid manual edits ${label}.type`);
|
|
33643
|
+
}
|
|
33644
|
+
function parsePlacementFrame(value, label) {
|
|
33645
|
+
if (!isRecord2(value)) throw new Error(`Invalid manual edits ${label}`);
|
|
33646
|
+
return {
|
|
33647
|
+
center: parsePathPoint(value.center, `${label}.center`),
|
|
33648
|
+
width: requireFiniteNumber2(value.width, `${label}.width`),
|
|
33649
|
+
height: requireFiniteNumber2(value.height, `${label}.height`)
|
|
33650
|
+
};
|
|
33651
|
+
}
|
|
33652
|
+
function parsePlacementZone(value, label) {
|
|
33653
|
+
if (!isRecord2(value)) throw new Error(`Invalid manual edits ${label}`);
|
|
33654
|
+
return {
|
|
33655
|
+
...parsePlacementFrame(value, label),
|
|
33656
|
+
id: requireString4(value.id, `${label}.id`),
|
|
33657
|
+
label: optionalString3(value.label)
|
|
33658
|
+
};
|
|
33659
|
+
}
|
|
33660
|
+
function parsePlacementRules(value, label) {
|
|
33661
|
+
if (!isRecord2(value)) throw new Error(`Invalid manual edits ${label}`);
|
|
33662
|
+
return {
|
|
33663
|
+
bounds: parsePlacementRuleMode(value.bounds, `${label}.bounds`),
|
|
33664
|
+
collisions: parsePlacementRuleMode(value.collisions, `${label}.collisions`),
|
|
33665
|
+
snap: requireFiniteNumber2(value.snap, `${label}.snap`)
|
|
33666
|
+
};
|
|
33667
|
+
}
|
|
33668
|
+
function parsePlacementItem(value, label) {
|
|
33669
|
+
if (!isRecord2(value)) throw new Error(`Invalid manual edits ${label}`);
|
|
33670
|
+
return {
|
|
33671
|
+
id: requireString4(value.id, `${label}.id`),
|
|
33672
|
+
label: optionalString3(value.label),
|
|
33673
|
+
footprint: parsePlacementFootprint(value.footprint, `${label}.footprint`),
|
|
33674
|
+
x: requireFiniteNumber2(value.x, `${label}.x`),
|
|
33675
|
+
y: requireFiniteNumber2(value.y, `${label}.y`),
|
|
33676
|
+
angle: requireFiniteNumber2(value.angle, `${label}.angle`),
|
|
33677
|
+
zone: optionalString3(value.zone),
|
|
33678
|
+
locked: value.locked === void 0 ? void 0 : Boolean(value.locked)
|
|
33679
|
+
};
|
|
33680
|
+
}
|
|
33681
|
+
function parseManualParam(value, label) {
|
|
33682
|
+
if (!isRecord2(value)) throw new Error(`Invalid manual edits ${label}`);
|
|
33683
|
+
const kind = requireString4(value.kind, `${label}.kind`);
|
|
33684
|
+
const name = requireString4(value.name, `${label}.name`);
|
|
33685
|
+
if (kind === "path2d") {
|
|
33686
|
+
const minPoints = requireInteger(value.minPoints, `${label}.minPoints`);
|
|
33687
|
+
const maxPoints = requireInteger(value.maxPoints, `${label}.maxPoints`);
|
|
33688
|
+
if (minPoints < 0 || maxPoints < minPoints) throw new Error(`Invalid manual edits ${label} point range`);
|
|
33689
|
+
if (!Array.isArray(value.points)) throw new Error(`Invalid manual edits ${label}.points`);
|
|
33690
|
+
return {
|
|
33691
|
+
kind,
|
|
33692
|
+
name,
|
|
33693
|
+
closed: Boolean(value.closed),
|
|
33694
|
+
minPoints,
|
|
33695
|
+
maxPoints,
|
|
33696
|
+
unit: optionalString3(value.unit),
|
|
33697
|
+
points: value.points.map((point, index) => parsePathPoint(point, `${label}.points[${index}]`))
|
|
33698
|
+
};
|
|
33699
|
+
}
|
|
33700
|
+
if (kind === "spline2d") {
|
|
33701
|
+
const minPoints = requireInteger(value.minPoints, `${label}.minPoints`);
|
|
33702
|
+
const maxPoints = requireInteger(value.maxPoints, `${label}.maxPoints`);
|
|
33703
|
+
if (minPoints < 0 || maxPoints < minPoints) throw new Error(`Invalid manual edits ${label} point range`);
|
|
33704
|
+
if (!Array.isArray(value.points)) throw new Error(`Invalid manual edits ${label}.points`);
|
|
33705
|
+
const degree = requireInteger(value.degree, `${label}.degree`);
|
|
33706
|
+
return {
|
|
33707
|
+
kind,
|
|
33708
|
+
name,
|
|
33709
|
+
closed: Boolean(value.closed),
|
|
33710
|
+
degree,
|
|
33711
|
+
minPoints,
|
|
33712
|
+
maxPoints,
|
|
33713
|
+
unit: optionalString3(value.unit),
|
|
33714
|
+
points: value.points.map((point, index) => parseSplinePoint(point, `${label}.points[${index}]`))
|
|
33715
|
+
};
|
|
33716
|
+
}
|
|
33717
|
+
if (kind === "placement2d") {
|
|
33718
|
+
if (!Array.isArray(value.zones)) throw new Error(`Invalid manual edits ${label}.zones`);
|
|
33719
|
+
if (!Array.isArray(value.items)) throw new Error(`Invalid manual edits ${label}.items`);
|
|
33720
|
+
return {
|
|
33721
|
+
kind,
|
|
33722
|
+
name,
|
|
33723
|
+
frame: parsePlacementFrame(value.frame, `${label}.frame`),
|
|
33724
|
+
zones: value.zones.map((zone, index) => parsePlacementZone(zone, `${label}.zones[${index}]`)),
|
|
33725
|
+
rules: parsePlacementRules(value.rules, `${label}.rules`),
|
|
33726
|
+
unit: optionalString3(value.unit),
|
|
33727
|
+
items: value.items.map((item, index) => parsePlacementItem(item, `${label}.items[${index}]`))
|
|
33728
|
+
};
|
|
33729
|
+
}
|
|
33730
|
+
throw new Error(`Invalid manual edits ${label}.kind`);
|
|
33731
|
+
}
|
|
33732
|
+
function parseManualParams(value) {
|
|
33733
|
+
if (!Array.isArray(value)) throw new Error("Invalid manual edits manualParams");
|
|
33734
|
+
return value.map((manualParam, index) => parseManualParam(manualParam, `manualParams[${index}]`));
|
|
33735
|
+
}
|
|
33736
|
+
function parseManualParamEditsDraft(value) {
|
|
33737
|
+
if (!isRecord2(value)) throw new Error("Invalid manual edits draft");
|
|
33738
|
+
if (value.kind !== MANUAL_PARAM_EDITS_DRAFT_KIND) throw new Error("Invalid manual edits draft kind");
|
|
33739
|
+
return {
|
|
33740
|
+
kind: MANUAL_PARAM_EDITS_DRAFT_KIND,
|
|
33741
|
+
filePath: parseFilePath(value.filePath),
|
|
33742
|
+
overrides: parseOverrides(value.overrides ?? {}),
|
|
33743
|
+
manualParams: parseManualParams(value.manualParams)
|
|
33744
|
+
};
|
|
33745
|
+
}
|
|
33746
|
+
function parseManualParamEditsPayload(value) {
|
|
33747
|
+
if (!isRecord2(value)) throw new Error("Invalid manual edits payload");
|
|
33748
|
+
if (value.kind !== MANUAL_PARAM_EDITS_KIND) throw new Error("Invalid manual edits kind");
|
|
33749
|
+
const revision = requireInteger(value.revision, "revision");
|
|
33750
|
+
if (revision < 1) throw new Error("Invalid manual edits revision");
|
|
33751
|
+
return {
|
|
33752
|
+
kind: MANUAL_PARAM_EDITS_KIND,
|
|
33753
|
+
filePath: parseFilePath(value.filePath),
|
|
33754
|
+
revision,
|
|
33755
|
+
submittedAt: requireString4(value.submittedAt, "submittedAt"),
|
|
33756
|
+
overrides: parseOverrides(value.overrides ?? {}),
|
|
33757
|
+
manualParams: parseManualParams(value.manualParams)
|
|
33758
|
+
};
|
|
33759
|
+
}
|
|
33760
|
+
function createSubmittedManualParamEdits(draftValue, previousRevision, submittedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
33761
|
+
const draft = parseManualParamEditsDraft(draftValue);
|
|
33762
|
+
return {
|
|
33763
|
+
kind: MANUAL_PARAM_EDITS_KIND,
|
|
33764
|
+
filePath: draft.filePath,
|
|
33765
|
+
revision: Math.max(0, previousRevision ?? 0) + 1,
|
|
33766
|
+
submittedAt,
|
|
33767
|
+
overrides: draft.overrides,
|
|
33768
|
+
manualParams: draft.manualParams
|
|
33769
|
+
};
|
|
33770
|
+
}
|
|
33771
|
+
function formatManualParamEditsText(payload, source) {
|
|
33772
|
+
const lines = [
|
|
33773
|
+
`Manual parameter edits for ${payload.filePath}`,
|
|
33774
|
+
`Revision: ${payload.revision}${source ? ` (${source})` : ""}`,
|
|
33775
|
+
`Submitted: ${payload.submittedAt}`,
|
|
33776
|
+
`Changed override keys: ${Object.keys(payload.overrides).length}`,
|
|
33777
|
+
`Manual controls: ${payload.manualParams.length}`,
|
|
33778
|
+
"",
|
|
33779
|
+
"Apply these by updating the matching Param.path2d(...), Param.spline2d(...), and Param.placement2d(...) defaults in source.",
|
|
33780
|
+
"Use `--format json` for exact point, G-continuity, footprint, and zone data."
|
|
33781
|
+
];
|
|
33782
|
+
if (payload.manualParams.length > 0) {
|
|
33783
|
+
lines.push("");
|
|
33784
|
+
for (const edit of payload.manualParams) {
|
|
33785
|
+
if (edit.kind === "path2d") {
|
|
33786
|
+
lines.push(
|
|
33787
|
+
`- path2d ${edit.name}: ${edit.points.length} ${edit.closed ? "outline" : "centerline"} point${edit.points.length === 1 ? "" : "s"}`
|
|
33788
|
+
);
|
|
33789
|
+
} else if (edit.kind === "spline2d") {
|
|
33790
|
+
const gCounts = edit.points.reduce(
|
|
33791
|
+
(counts, point) => ({ ...counts, [point.g]: counts[point.g] + 1 }),
|
|
33792
|
+
{ G0: 0, G1: 0, G2: 0 }
|
|
33793
|
+
);
|
|
33794
|
+
lines.push(
|
|
33795
|
+
`- spline2d ${edit.name}: ${edit.points.length} point${edit.points.length === 1 ? "" : "s"}, degree ${edit.degree}, G0 ${gCounts.G0}, G1 ${gCounts.G1}, G2 ${gCounts.G2}`
|
|
33796
|
+
);
|
|
33797
|
+
} else {
|
|
33798
|
+
const zoneCount = edit.zones.length;
|
|
33799
|
+
lines.push(
|
|
33800
|
+
`- placement2d ${edit.name}: ${edit.items.length} item${edit.items.length === 1 ? "" : "s"}, ${zoneCount} zone${zoneCount === 1 ? "" : "s"}, collisions ${edit.rules.collisions}`
|
|
33801
|
+
);
|
|
33802
|
+
}
|
|
33803
|
+
}
|
|
33804
|
+
}
|
|
33805
|
+
return lines.join("\n");
|
|
33806
|
+
}
|
|
33807
|
+
|
|
33808
|
+
// cli/manual-param-edits-storage.ts
|
|
33809
|
+
var MANUAL_PARAM_EDITS_ROOT = path3.join(".forgecad", "manual-param-edits", "files");
|
|
33810
|
+
function localManualParamEditsPath(projectRoot, filePath) {
|
|
33811
|
+
const normalizedFilePath = filePath.split("\\").join("/");
|
|
33812
|
+
const contextRoot = path3.resolve(projectRoot, MANUAL_PARAM_EDITS_ROOT);
|
|
33813
|
+
const resolved = path3.resolve(contextRoot, `${normalizedFilePath}.json`);
|
|
33814
|
+
if (!resolved.startsWith(contextRoot + path3.sep) && resolved !== contextRoot) {
|
|
33815
|
+
throw new Error(`Invalid manual edits file path: ${filePath}`);
|
|
33816
|
+
}
|
|
33817
|
+
return resolved;
|
|
33818
|
+
}
|
|
33819
|
+
function readLocalManualParamEdits(projectRoot, filePath) {
|
|
33820
|
+
const contextPath = localManualParamEditsPath(projectRoot, filePath);
|
|
33821
|
+
if (!fs2.existsSync(contextPath)) return null;
|
|
33822
|
+
return parseManualParamEditsPayload(JSON.parse(fs2.readFileSync(contextPath, "utf-8")));
|
|
33823
|
+
}
|
|
33824
|
+
function writeSubmittedLocalManualParamEdits(projectRoot, draft) {
|
|
33825
|
+
const draftFilePath = typeof draft === "object" && draft !== null && "filePath" in draft ? String(draft.filePath) : "";
|
|
33826
|
+
const existing = draftFilePath ? readLocalManualParamEdits(projectRoot, draftFilePath) : null;
|
|
33827
|
+
const context = createSubmittedManualParamEdits(draft, existing?.revision ?? null);
|
|
33828
|
+
const contextPath = localManualParamEditsPath(projectRoot, context.filePath);
|
|
33829
|
+
fs2.mkdirSync(path3.dirname(contextPath), { recursive: true });
|
|
33830
|
+
const tempPath = `${contextPath}.${process.pid}.${Date.now()}.tmp`;
|
|
33831
|
+
fs2.writeFileSync(tempPath, JSON.stringify(context, null, 2) + "\n", "utf-8");
|
|
33832
|
+
fs2.renameSync(tempPath, contextPath);
|
|
33833
|
+
return context;
|
|
33834
|
+
}
|
|
33835
|
+
|
|
33531
33836
|
// cli/forge-studio-server.ts
|
|
33532
33837
|
var PROJECT_FILE_EXTS = [
|
|
33533
33838
|
".forge.js",
|
|
@@ -33583,14 +33888,14 @@ var MIME = {
|
|
|
33583
33888
|
".md": "text/plain; charset=utf-8"
|
|
33584
33889
|
};
|
|
33585
33890
|
function slugFromDir(dir) {
|
|
33586
|
-
const name =
|
|
33891
|
+
const name = path4.basename(dir);
|
|
33587
33892
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "project";
|
|
33588
33893
|
}
|
|
33589
33894
|
function buildProjectRegistry(dirs) {
|
|
33590
33895
|
const projects = [];
|
|
33591
33896
|
const usedSlugs = /* @__PURE__ */ new Set();
|
|
33592
33897
|
for (const dir of dirs) {
|
|
33593
|
-
const absDir =
|
|
33898
|
+
const absDir = path4.resolve(dir);
|
|
33594
33899
|
let slug2 = slugFromDir(absDir);
|
|
33595
33900
|
if (usedSlugs.has(slug2)) {
|
|
33596
33901
|
let i = 2;
|
|
@@ -33601,7 +33906,7 @@ function buildProjectRegistry(dirs) {
|
|
|
33601
33906
|
projects.push({
|
|
33602
33907
|
id: slug2,
|
|
33603
33908
|
// Stable, URL-safe ID
|
|
33604
|
-
name:
|
|
33909
|
+
name: path4.basename(absDir),
|
|
33605
33910
|
slug: slug2,
|
|
33606
33911
|
dir: absDir
|
|
33607
33912
|
});
|
|
@@ -33640,19 +33945,19 @@ function readBinaryBody(req) {
|
|
|
33640
33945
|
});
|
|
33641
33946
|
}
|
|
33642
33947
|
function scanProjectFiles(projectDir) {
|
|
33643
|
-
const abs =
|
|
33948
|
+
const abs = path4.resolve(projectDir);
|
|
33644
33949
|
const files = {};
|
|
33645
33950
|
const folderSet = /* @__PURE__ */ new Set();
|
|
33646
33951
|
function scan(dir, prefix) {
|
|
33647
|
-
for (const item of
|
|
33952
|
+
for (const item of fs3.readdirSync(dir, { withFileTypes: true })) {
|
|
33648
33953
|
if (item.name.startsWith(".")) continue;
|
|
33649
33954
|
const rel = prefix ? `${prefix}/${item.name}` : item.name;
|
|
33650
33955
|
if (item.isDirectory()) {
|
|
33651
33956
|
if (shouldSkipProjectDirectory2(item.name)) continue;
|
|
33652
33957
|
folderSet.add(rel);
|
|
33653
|
-
scan(
|
|
33958
|
+
scan(path4.join(dir, item.name), rel);
|
|
33654
33959
|
} else if (item.isFile() && !isLocalProjectManifestPath(rel) && isProjectFile(item.name)) {
|
|
33655
|
-
files[rel] =
|
|
33960
|
+
files[rel] = fs3.readFileSync(path4.join(dir, item.name), "utf-8");
|
|
33656
33961
|
} else if (item.isFile() && isBinaryProjectFile(item.name)) {
|
|
33657
33962
|
files[rel] = "";
|
|
33658
33963
|
}
|
|
@@ -33665,17 +33970,17 @@ function scanProjectFiles(projectDir) {
|
|
|
33665
33970
|
return { files, folders: [...folderSet] };
|
|
33666
33971
|
}
|
|
33667
33972
|
function scanProjectFileListing(projectDir) {
|
|
33668
|
-
const abs =
|
|
33973
|
+
const abs = path4.resolve(projectDir);
|
|
33669
33974
|
const files = [];
|
|
33670
33975
|
const folders = [];
|
|
33671
33976
|
function scan(dir, prefix) {
|
|
33672
|
-
for (const item of
|
|
33977
|
+
for (const item of fs3.readdirSync(dir, { withFileTypes: true })) {
|
|
33673
33978
|
if (item.name.startsWith(".")) continue;
|
|
33674
33979
|
const rel = prefix ? `${prefix}/${item.name}` : item.name;
|
|
33675
33980
|
if (item.isDirectory()) {
|
|
33676
33981
|
if (shouldSkipProjectDirectory2(item.name)) continue;
|
|
33677
33982
|
folders.push(rel);
|
|
33678
|
-
scan(
|
|
33983
|
+
scan(path4.join(dir, item.name), rel);
|
|
33679
33984
|
} else if (item.isFile() && !isLocalProjectManifestPath(rel) && (isProjectFile(item.name) || isBinaryProjectFile(item.name))) {
|
|
33680
33985
|
files.push(rel);
|
|
33681
33986
|
}
|
|
@@ -33690,11 +33995,11 @@ function scanProjectFileListing(projectDir) {
|
|
|
33690
33995
|
function resolveProjectFile(projectDir, filename, opts = {}) {
|
|
33691
33996
|
if (!projectDir) throw new Error("No project directory");
|
|
33692
33997
|
if (!filename) throw new Error("Invalid file path");
|
|
33693
|
-
const abs =
|
|
33694
|
-
const filePath =
|
|
33998
|
+
const abs = path4.resolve(projectDir);
|
|
33999
|
+
const filePath = path4.isAbsolute(filename) ? path4.resolve(filename) : path4.resolve(abs, filename);
|
|
33695
34000
|
if (!isProjectFile(filePath) && !(opts.allowBinary && isBinaryProjectFile(filePath))) throw new Error("Invalid file type");
|
|
33696
|
-
const rel =
|
|
33697
|
-
if (rel.startsWith("..") ||
|
|
34001
|
+
const rel = path4.relative(abs, filePath);
|
|
34002
|
+
if (rel.startsWith("..") || path4.isAbsolute(rel)) {
|
|
33698
34003
|
throw new Error(`Path "${filename}" is outside the project root`);
|
|
33699
34004
|
}
|
|
33700
34005
|
if (isLocalProjectManifestPath(rel.replace(/\\/g, "/"))) throw new Error("Project manifest is not editable");
|
|
@@ -33703,31 +34008,31 @@ function resolveProjectFile(projectDir, filename, opts = {}) {
|
|
|
33703
34008
|
function resolveProjectDirectory(projectDir, dirPath) {
|
|
33704
34009
|
if (!projectDir) throw new Error("No project directory");
|
|
33705
34010
|
if (!dirPath) throw new Error("Invalid directory path");
|
|
33706
|
-
const abs =
|
|
33707
|
-
const resolved =
|
|
33708
|
-
const rel =
|
|
33709
|
-
if (rel.startsWith("..") ||
|
|
34011
|
+
const abs = path4.resolve(projectDir);
|
|
34012
|
+
const resolved = path4.resolve(abs, dirPath);
|
|
34013
|
+
const rel = path4.relative(abs, resolved);
|
|
34014
|
+
if (rel.startsWith("..") || path4.isAbsolute(rel)) {
|
|
33710
34015
|
throw new Error(`Path "${dirPath}" is outside the project root`);
|
|
33711
34016
|
}
|
|
33712
34017
|
return { dirPath: resolved, dirname: rel.replace(/\\/g, "/") };
|
|
33713
34018
|
}
|
|
33714
34019
|
function serveStatic(distDir, req, res) {
|
|
33715
|
-
const absDistDir =
|
|
34020
|
+
const absDistDir = path4.resolve(distDir);
|
|
33716
34021
|
const urlPath = decodeURIComponent((req.url ?? "/").split("?")[0]);
|
|
33717
|
-
let filePath =
|
|
33718
|
-
if (!filePath.startsWith(absDistDir +
|
|
34022
|
+
let filePath = path4.resolve(absDistDir, urlPath === "/" ? "index.html" : "." + urlPath);
|
|
34023
|
+
if (!filePath.startsWith(absDistDir + path4.sep) && filePath !== absDistDir) {
|
|
33719
34024
|
res.statusCode = 403;
|
|
33720
34025
|
res.end("Forbidden");
|
|
33721
34026
|
return true;
|
|
33722
34027
|
}
|
|
33723
|
-
if (!
|
|
33724
|
-
filePath =
|
|
34028
|
+
if (!fs3.existsSync(filePath) || !fs3.statSync(filePath).isFile()) {
|
|
34029
|
+
filePath = path4.join(absDistDir, "index.html");
|
|
33725
34030
|
}
|
|
33726
|
-
if (!
|
|
33727
|
-
const ext =
|
|
34031
|
+
if (!fs3.existsSync(filePath)) return false;
|
|
34032
|
+
const ext = path4.extname(filePath).toLowerCase();
|
|
33728
34033
|
const mime = MIME[ext] ?? "application/octet-stream";
|
|
33729
34034
|
const isText = mime.includes("charset");
|
|
33730
|
-
const content =
|
|
34035
|
+
const content = fs3.readFileSync(filePath, isText ? "utf-8" : null);
|
|
33731
34036
|
res.statusCode = 200;
|
|
33732
34037
|
res.setHeader("Content-Type", mime);
|
|
33733
34038
|
res.setHeader("Cache-Control", urlPath.startsWith("/assets/") ? "max-age=31536000,immutable" : "no-cache");
|
|
@@ -33845,18 +34150,18 @@ data: ${JSON.stringify(data)}
|
|
|
33845
34150
|
});
|
|
33846
34151
|
}
|
|
33847
34152
|
for (const proj of projects) {
|
|
33848
|
-
const abs =
|
|
34153
|
+
const abs = path4.resolve(proj.dir);
|
|
33849
34154
|
const watcher = chokidar.watch(abs, {
|
|
33850
34155
|
ignoreInitial: true,
|
|
33851
|
-
ignored: (candidate) =>
|
|
34156
|
+
ignored: (candidate) => path4.relative(abs, candidate).split(/[\\/]/).some(shouldSkipProjectDirectory2)
|
|
33852
34157
|
});
|
|
33853
34158
|
watcherReadyPromises.push(waitForWatcherReady(watcher));
|
|
33854
34159
|
watcher.on("add", (f2) => {
|
|
33855
|
-
const rel =
|
|
34160
|
+
const rel = path4.relative(abs, f2).replace(/\\/g, "/");
|
|
33856
34161
|
if (isLocalProjectManifestPath(rel)) return;
|
|
33857
34162
|
if (isProjectFile(f2)) {
|
|
33858
34163
|
try {
|
|
33859
|
-
broadcastToProject(proj.id, "change", { filename: rel, content:
|
|
34164
|
+
broadcastToProject(proj.id, "change", { filename: rel, content: fs3.readFileSync(f2, "utf-8") });
|
|
33860
34165
|
} catch {
|
|
33861
34166
|
}
|
|
33862
34167
|
} else if (isBinaryProjectFile(f2)) {
|
|
@@ -33864,11 +34169,11 @@ data: ${JSON.stringify(data)}
|
|
|
33864
34169
|
}
|
|
33865
34170
|
});
|
|
33866
34171
|
watcher.on("change", (f2) => {
|
|
33867
|
-
const rel =
|
|
34172
|
+
const rel = path4.relative(abs, f2).replace(/\\/g, "/");
|
|
33868
34173
|
if (isLocalProjectManifestPath(rel)) return;
|
|
33869
34174
|
if (isProjectFile(f2)) {
|
|
33870
34175
|
try {
|
|
33871
|
-
broadcastToProject(proj.id, "change", { filename: rel, content:
|
|
34176
|
+
broadcastToProject(proj.id, "change", { filename: rel, content: fs3.readFileSync(f2, "utf-8") });
|
|
33872
34177
|
} catch {
|
|
33873
34178
|
}
|
|
33874
34179
|
} else if (isBinaryProjectFile(f2)) {
|
|
@@ -33876,7 +34181,7 @@ data: ${JSON.stringify(data)}
|
|
|
33876
34181
|
}
|
|
33877
34182
|
});
|
|
33878
34183
|
watcher.on("unlink", (f2) => {
|
|
33879
|
-
const rel =
|
|
34184
|
+
const rel = path4.relative(abs, f2).replace(/\\/g, "/");
|
|
33880
34185
|
if (isLocalProjectManifestPath(rel)) return;
|
|
33881
34186
|
if (!isProjectFile(f2) && !isBinaryProjectFile(f2)) return;
|
|
33882
34187
|
broadcastToProject(proj.id, "delete", { filename: rel });
|
|
@@ -34035,11 +34340,11 @@ data: ${JSON.stringify(data)}
|
|
|
34035
34340
|
}
|
|
34036
34341
|
try {
|
|
34037
34342
|
const resolved = resolveProjectFile(proj.dir, filename);
|
|
34038
|
-
if (!
|
|
34343
|
+
if (!fs3.existsSync(resolved.filePath)) {
|
|
34039
34344
|
sendJson(res, 404, { error: `File not found: ${filename}` });
|
|
34040
34345
|
return;
|
|
34041
34346
|
}
|
|
34042
|
-
const content =
|
|
34347
|
+
const content = fs3.readFileSync(resolved.filePath, "utf-8");
|
|
34043
34348
|
sendJson(res, 200, { filename: resolved.filename, content });
|
|
34044
34349
|
} catch (e) {
|
|
34045
34350
|
sendJson(res, 400, { error: e.message });
|
|
@@ -34063,7 +34368,7 @@ data: ${JSON.stringify(data)}
|
|
|
34063
34368
|
}
|
|
34064
34369
|
try {
|
|
34065
34370
|
const resolved = resolveProjectFile(proj.dir, entry);
|
|
34066
|
-
if (!
|
|
34371
|
+
if (!fs3.existsSync(resolved.filePath)) {
|
|
34067
34372
|
sendJson(res, 404, { error: `File not found: ${entry}` });
|
|
34068
34373
|
return;
|
|
34069
34374
|
}
|
|
@@ -34123,6 +34428,54 @@ data: ${JSON.stringify(data)}
|
|
|
34123
34428
|
return;
|
|
34124
34429
|
}
|
|
34125
34430
|
}
|
|
34431
|
+
{
|
|
34432
|
+
const m = matchProjectRoute(url2, method, "GET", "/manual-param-edits");
|
|
34433
|
+
if (m) {
|
|
34434
|
+
const proj = lookupProject(m.projectId);
|
|
34435
|
+
if (!proj) {
|
|
34436
|
+
sendJson(res, 404, { error: "Project not found" });
|
|
34437
|
+
return;
|
|
34438
|
+
}
|
|
34439
|
+
const params = new URLSearchParams(m.query);
|
|
34440
|
+
const filePath = params.get("file");
|
|
34441
|
+
if (!filePath) {
|
|
34442
|
+
sendJson(res, 400, { error: "Missing file parameter" });
|
|
34443
|
+
return;
|
|
34444
|
+
}
|
|
34445
|
+
try {
|
|
34446
|
+
const resolved = resolveProjectFile(proj.dir, filePath);
|
|
34447
|
+
const context = readLocalManualParamEdits(proj.dir, resolved.filename);
|
|
34448
|
+
if (!context) {
|
|
34449
|
+
sendJson(res, 404, { error: `No submitted manual edits for ${resolved.filename}` });
|
|
34450
|
+
return;
|
|
34451
|
+
}
|
|
34452
|
+
sendJson(res, 200, { context });
|
|
34453
|
+
} catch (e) {
|
|
34454
|
+
sendJson(res, 400, { error: e.message });
|
|
34455
|
+
}
|
|
34456
|
+
return;
|
|
34457
|
+
}
|
|
34458
|
+
}
|
|
34459
|
+
{
|
|
34460
|
+
const m = matchProjectRoute(url2, method, "POST", "/manual-param-edits");
|
|
34461
|
+
if (m) {
|
|
34462
|
+
const proj = lookupProject(m.projectId);
|
|
34463
|
+
if (!proj) {
|
|
34464
|
+
sendJson(res, 404, { error: "Project not found" });
|
|
34465
|
+
return;
|
|
34466
|
+
}
|
|
34467
|
+
readJsonBody(req).then((body) => {
|
|
34468
|
+
if (!body || typeof body.filePath !== "string") {
|
|
34469
|
+
sendJson(res, 400, { error: "Invalid manual edits draft" });
|
|
34470
|
+
return;
|
|
34471
|
+
}
|
|
34472
|
+
const resolved = resolveProjectFile(proj.dir, body.filePath);
|
|
34473
|
+
const context = writeSubmittedLocalManualParamEdits(proj.dir, { ...body, filePath: resolved.filename });
|
|
34474
|
+
sendJson(res, 200, { context });
|
|
34475
|
+
}).catch((e) => sendJson(res, 400, { error: e.message }));
|
|
34476
|
+
return;
|
|
34477
|
+
}
|
|
34478
|
+
}
|
|
34126
34479
|
{
|
|
34127
34480
|
const m = method === "GET" ? matchSSERoute(url2, "/watch") : null;
|
|
34128
34481
|
if (m) {
|
|
@@ -34171,8 +34524,8 @@ data: ${JSON.stringify(data)}
|
|
|
34171
34524
|
return;
|
|
34172
34525
|
}
|
|
34173
34526
|
const resolved = resolveProjectFile(proj.dir, filename);
|
|
34174
|
-
|
|
34175
|
-
|
|
34527
|
+
fs3.mkdirSync(path4.dirname(resolved.filePath), { recursive: true });
|
|
34528
|
+
fs3.writeFileSync(resolved.filePath, content, "utf-8");
|
|
34176
34529
|
sendJson(res, 200, { success: true });
|
|
34177
34530
|
}).catch((e) => sendJson(res, 500, { error: e.message }));
|
|
34178
34531
|
return;
|
|
@@ -34198,8 +34551,8 @@ data: ${JSON.stringify(data)}
|
|
|
34198
34551
|
sendJson(res, 400, { error: `Not a binary import file: ${filename}` });
|
|
34199
34552
|
return;
|
|
34200
34553
|
}
|
|
34201
|
-
|
|
34202
|
-
|
|
34554
|
+
fs3.mkdirSync(path4.dirname(resolved.filePath), { recursive: true });
|
|
34555
|
+
fs3.writeFileSync(resolved.filePath, body);
|
|
34203
34556
|
sendJson(res, 200, { success: true });
|
|
34204
34557
|
}).catch((e) => sendJson(res, 500, { error: e.message }));
|
|
34205
34558
|
return;
|
|
@@ -34220,8 +34573,8 @@ data: ${JSON.stringify(data)}
|
|
|
34220
34573
|
return;
|
|
34221
34574
|
}
|
|
34222
34575
|
const resolved = resolveProjectFile(proj.dir, filename, { allowBinary: true });
|
|
34223
|
-
if (
|
|
34224
|
-
|
|
34576
|
+
if (fs3.existsSync(resolved.filePath)) {
|
|
34577
|
+
fs3.unlinkSync(resolved.filePath);
|
|
34225
34578
|
}
|
|
34226
34579
|
sendJson(res, 200, { success: true });
|
|
34227
34580
|
}).catch((e) => sendJson(res, 500, { error: e.message }));
|
|
@@ -34242,12 +34595,12 @@ data: ${JSON.stringify(data)}
|
|
|
34242
34595
|
sendJson(res, 400, { error: "Invalid request" });
|
|
34243
34596
|
return;
|
|
34244
34597
|
}
|
|
34245
|
-
const absDir =
|
|
34246
|
-
if (!absDir.startsWith(proj.dir +
|
|
34598
|
+
const absDir = path4.resolve(proj.dir, dirPath);
|
|
34599
|
+
if (!absDir.startsWith(proj.dir + path4.sep) && absDir !== proj.dir) {
|
|
34247
34600
|
sendJson(res, 403, { error: "Path outside project root" });
|
|
34248
34601
|
return;
|
|
34249
34602
|
}
|
|
34250
|
-
|
|
34603
|
+
fs3.mkdirSync(absDir, { recursive: true });
|
|
34251
34604
|
sendJson(res, 200, { success: true });
|
|
34252
34605
|
}).catch((e) => sendJson(res, 500, { error: e.message }));
|
|
34253
34606
|
return;
|
|
@@ -34278,15 +34631,15 @@ data: ${JSON.stringify(data)}
|
|
|
34278
34631
|
sendJson(res, 400, { error: "Cannot delete project root" });
|
|
34279
34632
|
return;
|
|
34280
34633
|
}
|
|
34281
|
-
if (!
|
|
34634
|
+
if (!fs3.existsSync(resolved.dirPath)) {
|
|
34282
34635
|
sendJson(res, 200, { success: true });
|
|
34283
34636
|
return;
|
|
34284
34637
|
}
|
|
34285
|
-
if (!
|
|
34638
|
+
if (!fs3.statSync(resolved.dirPath).isDirectory()) {
|
|
34286
34639
|
sendJson(res, 400, { error: "Path is not a directory" });
|
|
34287
34640
|
return;
|
|
34288
34641
|
}
|
|
34289
|
-
|
|
34642
|
+
fs3.rmSync(resolved.dirPath, { recursive: true, force: true });
|
|
34290
34643
|
sendJson(res, 200, { success: true });
|
|
34291
34644
|
}).catch((e) => sendJson(res, 500, { error: e.message }));
|
|
34292
34645
|
return;
|
|
@@ -34308,16 +34661,16 @@ data: ${JSON.stringify(data)}
|
|
|
34308
34661
|
}
|
|
34309
34662
|
const fromResolved = resolveProjectFile(proj.dir, from, { allowBinary: true });
|
|
34310
34663
|
const toResolved = resolveProjectFile(proj.dir, to, { allowBinary: true });
|
|
34311
|
-
if (!
|
|
34664
|
+
if (!fs3.existsSync(fromResolved.filePath)) {
|
|
34312
34665
|
sendJson(res, 404, { error: `File not found: ${from}` });
|
|
34313
34666
|
return;
|
|
34314
34667
|
}
|
|
34315
|
-
if (
|
|
34668
|
+
if (fs3.existsSync(toResolved.filePath)) {
|
|
34316
34669
|
sendJson(res, 409, { error: `Destination already exists: ${to}` });
|
|
34317
34670
|
return;
|
|
34318
34671
|
}
|
|
34319
|
-
|
|
34320
|
-
|
|
34672
|
+
fs3.mkdirSync(path4.dirname(toResolved.filePath), { recursive: true });
|
|
34673
|
+
fs3.renameSync(fromResolved.filePath, toResolved.filePath);
|
|
34321
34674
|
sendJson(res, 200, { success: true });
|
|
34322
34675
|
}).catch((e) => sendJson(res, 500, { error: e.message }));
|
|
34323
34676
|
return;
|
|
@@ -34337,10 +34690,10 @@ data: ${JSON.stringify(data)}
|
|
|
34337
34690
|
sendJson(res, 400, { error: "Missing path parameter" });
|
|
34338
34691
|
return;
|
|
34339
34692
|
}
|
|
34340
|
-
const absProject =
|
|
34341
|
-
const filePath =
|
|
34342
|
-
const rel =
|
|
34343
|
-
if (rel.startsWith("..") ||
|
|
34693
|
+
const absProject = path4.resolve(proj.dir);
|
|
34694
|
+
const filePath = path4.resolve(absProject, filename);
|
|
34695
|
+
const rel = path4.relative(absProject, filePath);
|
|
34696
|
+
if (rel.startsWith("..") || path4.isAbsolute(rel)) {
|
|
34344
34697
|
sendJson(res, 403, { error: "Path outside project root" });
|
|
34345
34698
|
return;
|
|
34346
34699
|
}
|
|
@@ -34348,11 +34701,11 @@ data: ${JSON.stringify(data)}
|
|
|
34348
34701
|
sendJson(res, 400, { error: `Not a binary import file: ${filename}` });
|
|
34349
34702
|
return;
|
|
34350
34703
|
}
|
|
34351
|
-
if (!
|
|
34704
|
+
if (!fs3.existsSync(filePath)) {
|
|
34352
34705
|
sendJson(res, 404, { error: `File not found: ${filename}` });
|
|
34353
34706
|
return;
|
|
34354
34707
|
}
|
|
34355
|
-
const data =
|
|
34708
|
+
const data = fs3.readFileSync(filePath);
|
|
34356
34709
|
res.writeHead(200, { "Content-Type": "application/octet-stream" });
|
|
34357
34710
|
res.end(data);
|
|
34358
34711
|
return;
|
|
@@ -34369,6 +34722,16 @@ data: ${JSON.stringify(data)}
|
|
|
34369
34722
|
sendJson(res, 200, { linked: false });
|
|
34370
34723
|
return;
|
|
34371
34724
|
}
|
|
34725
|
+
if (!hasRemoteProject(manifest)) {
|
|
34726
|
+
sendJson(res, 200, {
|
|
34727
|
+
linked: false,
|
|
34728
|
+
initialized: true,
|
|
34729
|
+
slug: manifest.slug,
|
|
34730
|
+
server: manifest.server,
|
|
34731
|
+
message: "Run `forgecad project push` to create the hosted project."
|
|
34732
|
+
});
|
|
34733
|
+
return;
|
|
34734
|
+
}
|
|
34372
34735
|
const auth = readStoredAuth();
|
|
34373
34736
|
if (!auth) {
|
|
34374
34737
|
sendJson(res, 200, { linked: true, authenticated: false, slug: manifest.slug, server: manifest.server });
|
|
@@ -34410,6 +34773,10 @@ data: ${JSON.stringify(data)}
|
|
|
34410
34773
|
sendJson(res, 400, { error: "Not linked to a remote project" });
|
|
34411
34774
|
return;
|
|
34412
34775
|
}
|
|
34776
|
+
if (!hasRemoteProject(manifest)) {
|
|
34777
|
+
sendJson(res, 400, { error: "Project has not been pushed yet. Run `forgecad project push` first." });
|
|
34778
|
+
return;
|
|
34779
|
+
}
|
|
34413
34780
|
const auth = readStoredAuth();
|
|
34414
34781
|
if (!auth) {
|
|
34415
34782
|
sendJson(res, 401, { error: "Not authenticated. Run `forgecad login` first." });
|
|
@@ -34580,7 +34947,7 @@ async function runStudioCli(argv = process.argv.slice(2)) {
|
|
|
34580
34947
|
// cli/forge-context.ts
|
|
34581
34948
|
import { existsSync as existsSync22 } from "fs";
|
|
34582
34949
|
import { mkdir as mkdir4, rename as rename2, rm as rm2, writeFile as writeFile8 } from "fs/promises";
|
|
34583
|
-
import
|
|
34950
|
+
import path5 from "path";
|
|
34584
34951
|
function usage21(command) {
|
|
34585
34952
|
if (command === "render") return "Usage: forgecad context render <model.forge.js> [--out <dir>] [--force] [--size <px>]";
|
|
34586
34953
|
return `Usage: forgecad context ${command} <model.forge.js> [--format text|json]`;
|
|
@@ -34649,16 +35016,16 @@ ${usage21("render")}`);
|
|
|
34649
35016
|
return { filePath, outDir, force, size };
|
|
34650
35017
|
}
|
|
34651
35018
|
function resolveProjectFile2(filePath) {
|
|
34652
|
-
const absFilePath =
|
|
35019
|
+
const absFilePath = path5.resolve(filePath);
|
|
34653
35020
|
if (!existsSync22(absFilePath)) throw new Error(`File not found: ${filePath}`);
|
|
34654
35021
|
const projectRoot = findProjectRoot(absFilePath);
|
|
34655
|
-
const fileName =
|
|
34656
|
-
if (fileName.startsWith("..") ||
|
|
35022
|
+
const fileName = path5.relative(projectRoot, absFilePath).replace(/\\/g, "/");
|
|
35023
|
+
if (fileName.startsWith("..") || path5.isAbsolute(fileName)) throw new Error(`File is outside project root: ${filePath}`);
|
|
34657
35024
|
return { projectRoot, fileName };
|
|
34658
35025
|
}
|
|
34659
35026
|
async function readRemoteAgentContext(projectRoot, fileName) {
|
|
34660
35027
|
const manifest = readManifest(projectRoot);
|
|
34661
|
-
if (!manifest) return null;
|
|
35028
|
+
if (!manifest || !hasRemoteProject(manifest)) return null;
|
|
34662
35029
|
const res = await authenticatedFetch(`/api/projects/${manifest.projectId}/agent-context?file=${encodeURIComponent(fileName)}`);
|
|
34663
35030
|
if (res.status === 404) return null;
|
|
34664
35031
|
if (!res.ok) {
|
|
@@ -34707,9 +35074,9 @@ function outputContextStem(fileName) {
|
|
|
34707
35074
|
return fileName.replace(/\.js$/i, "").split("\\").join("/");
|
|
34708
35075
|
}
|
|
34709
35076
|
async function allocateBundleDir(projectRoot, fileName, revision, outDir, force) {
|
|
34710
|
-
const requested = outDir ?
|
|
35077
|
+
const requested = outDir ? path5.resolve(outDir) : (() => {
|
|
34711
35078
|
const { dateDir, timeStem } = timestampParts();
|
|
34712
|
-
return
|
|
35079
|
+
return path5.resolve(projectRoot, "outputs", "context", outputContextStem(fileName), dateDir, `${timeStem}-rev${revision}`);
|
|
34713
35080
|
})();
|
|
34714
35081
|
if (force) {
|
|
34715
35082
|
await rm2(requested, { recursive: true, force: true });
|
|
@@ -34727,7 +35094,7 @@ async function allocateBundleDir(projectRoot, fileName, revision, outDir, force)
|
|
|
34727
35094
|
throw new Error(`Could not allocate a non-colliding context bundle directory near ${requested}`);
|
|
34728
35095
|
}
|
|
34729
35096
|
function bundleRelativePath(projectRoot, bundleDir) {
|
|
34730
|
-
return
|
|
35097
|
+
return path5.relative(projectRoot, bundleDir).split(path5.sep).join("/");
|
|
34731
35098
|
}
|
|
34732
35099
|
function renderRequirePath(fileName) {
|
|
34733
35100
|
return `./${fileName.split("\\").join("/")}`;
|
|
@@ -34789,13 +35156,13 @@ return rendered;
|
|
|
34789
35156
|
`;
|
|
34790
35157
|
}
|
|
34791
35158
|
async function writeContextRenderWrapper(projectRoot, fileName, context) {
|
|
34792
|
-
const wrapperPath =
|
|
35159
|
+
const wrapperPath = path5.join(projectRoot, `.forgecad-context-render-${process.pid}-${Date.now()}.forge.js`);
|
|
34793
35160
|
await writeFile8(wrapperPath, buildContextRenderWrapper(fileName, context), "utf-8");
|
|
34794
35161
|
return wrapperPath;
|
|
34795
35162
|
}
|
|
34796
35163
|
async function renderContextViews(wrapperPath, viewsDir, size) {
|
|
34797
35164
|
await mkdir4(viewsDir, { recursive: true });
|
|
34798
|
-
const outputBase =
|
|
35165
|
+
const outputBase = path5.join(viewsDir, "view.png");
|
|
34799
35166
|
await runRenderCli([
|
|
34800
35167
|
wrapperPath,
|
|
34801
35168
|
outputBase,
|
|
@@ -34812,11 +35179,11 @@ async function renderContextViews(wrapperPath, viewsDir, size) {
|
|
|
34812
35179
|
]);
|
|
34813
35180
|
const viewNames = ["iso", "front", "right", "top"];
|
|
34814
35181
|
for (const view of viewNames) {
|
|
34815
|
-
const produced =
|
|
34816
|
-
const target =
|
|
35182
|
+
const produced = path5.join(viewsDir, `view_${view}.png`);
|
|
35183
|
+
const target = path5.join(viewsDir, `${view}.png`);
|
|
34817
35184
|
if (existsSync22(produced)) await rename2(produced, target);
|
|
34818
35185
|
}
|
|
34819
|
-
return viewNames.filter((view) => existsSync22(
|
|
35186
|
+
return viewNames.filter((view) => existsSync22(path5.join(viewsDir, `${view}.png`)));
|
|
34820
35187
|
}
|
|
34821
35188
|
function briefMarkdown(context, views) {
|
|
34822
35189
|
const lines = [
|
|
@@ -34845,11 +35212,11 @@ function briefMarkdown(context, views) {
|
|
|
34845
35212
|
async function writeContextBundle(projectRoot, bundleDir, context, views) {
|
|
34846
35213
|
const latestBundle = bundleRelativePath(projectRoot, bundleDir);
|
|
34847
35214
|
const updatedContext = updateLocalAgentContextLatestBundle(projectRoot, context.filePath, latestBundle);
|
|
34848
|
-
await writeFile8(
|
|
35215
|
+
await writeFile8(path5.join(bundleDir, "context.json"), `${JSON.stringify(updatedContext, null, 2)}
|
|
34849
35216
|
`, "utf-8");
|
|
34850
|
-
await writeFile8(
|
|
35217
|
+
await writeFile8(path5.join(bundleDir, "brief.md"), briefMarkdown(updatedContext, views), "utf-8");
|
|
34851
35218
|
await writeFile8(
|
|
34852
|
-
|
|
35219
|
+
path5.join(bundleDir, "manifest.json"),
|
|
34853
35220
|
`${JSON.stringify(
|
|
34854
35221
|
{
|
|
34855
35222
|
kind: "forgecad.agentContextBundle.v1",
|
|
@@ -34926,7 +35293,7 @@ async function runContextRenderCli(args = process.argv.slice(2)) {
|
|
|
34926
35293
|
throw new Error("context render currently requires a local submitted context sidecar.");
|
|
34927
35294
|
}
|
|
34928
35295
|
const bundleDir = await allocateBundleDir(loaded.projectRoot, loaded.filePath, loaded.context.revision, parsed.outDir, parsed.force);
|
|
34929
|
-
const viewsDir =
|
|
35296
|
+
const viewsDir = path5.join(bundleDir, "views");
|
|
34930
35297
|
wrapperPath = await writeContextRenderWrapper(loaded.projectRoot, loaded.filePath, loaded.context);
|
|
34931
35298
|
const views = await renderContextViews(wrapperPath, viewsDir, parsed.size);
|
|
34932
35299
|
if (views.length === 0) throw new Error("Context render did not produce any view PNGs.");
|
|
@@ -34936,7 +35303,7 @@ async function runContextRenderCli(args = process.argv.slice(2)) {
|
|
|
34936
35303
|
console.log(` File: ${updatedContext.filePath}`);
|
|
34937
35304
|
console.log(` Revision: ${updatedContext.revision}`);
|
|
34938
35305
|
console.log(` Bundle: ${bundleRelativePath(loaded.projectRoot, bundleDir)}`);
|
|
34939
|
-
console.log(` Brief: ${
|
|
35306
|
+
console.log(` Brief: ${path5.join(bundleRelativePath(loaded.projectRoot, bundleDir), "brief.md")}`);
|
|
34940
35307
|
} catch (error) {
|
|
34941
35308
|
console.error(error instanceof Error ? error.message : String(error));
|
|
34942
35309
|
process.exit(1);
|
|
@@ -34945,17 +35312,139 @@ async function runContextRenderCli(args = process.argv.slice(2)) {
|
|
|
34945
35312
|
}
|
|
34946
35313
|
}
|
|
34947
35314
|
|
|
35315
|
+
// cli/forge-manual-edits.ts
|
|
35316
|
+
import { existsSync as existsSync23 } from "fs";
|
|
35317
|
+
import path6 from "path";
|
|
35318
|
+
function usage22(command) {
|
|
35319
|
+
return `Usage: forgecad manual-edits ${command} <model.forge.js> [--format text|json]`;
|
|
35320
|
+
}
|
|
35321
|
+
function parseArgs19(command, args) {
|
|
35322
|
+
let filePath;
|
|
35323
|
+
let format = "text";
|
|
35324
|
+
for (let i = 0; i < args.length; i++) {
|
|
35325
|
+
const arg = args[i];
|
|
35326
|
+
if (arg === "--format") {
|
|
35327
|
+
const value = args[++i];
|
|
35328
|
+
if (value !== "text" && value !== "json") throw new Error(usage22(command));
|
|
35329
|
+
format = value;
|
|
35330
|
+
} else if (arg.startsWith("--format=")) {
|
|
35331
|
+
const value = arg.slice("--format=".length);
|
|
35332
|
+
if (value !== "text" && value !== "json") throw new Error(usage22(command));
|
|
35333
|
+
format = value;
|
|
35334
|
+
} else if (arg.startsWith("-")) {
|
|
35335
|
+
throw new Error(`Unknown option: ${arg}
|
|
35336
|
+
${usage22(command)}`);
|
|
35337
|
+
} else if (!filePath) {
|
|
35338
|
+
filePath = arg;
|
|
35339
|
+
} else {
|
|
35340
|
+
throw new Error(`Unexpected argument: ${arg}
|
|
35341
|
+
${usage22(command)}`);
|
|
35342
|
+
}
|
|
35343
|
+
}
|
|
35344
|
+
if (!filePath) throw new Error(usage22(command));
|
|
35345
|
+
return { filePath, format };
|
|
35346
|
+
}
|
|
35347
|
+
function resolveProjectFile3(filePath) {
|
|
35348
|
+
const absFilePath = path6.resolve(filePath);
|
|
35349
|
+
if (!existsSync23(absFilePath)) throw new Error(`File not found: ${filePath}`);
|
|
35350
|
+
const projectRoot = findProjectRoot(absFilePath);
|
|
35351
|
+
const fileName = path6.relative(projectRoot, absFilePath).replace(/\\/g, "/");
|
|
35352
|
+
if (fileName.startsWith("..") || path6.isAbsolute(fileName)) throw new Error(`File is outside project root: ${filePath}`);
|
|
35353
|
+
return { projectRoot, fileName };
|
|
35354
|
+
}
|
|
35355
|
+
async function readRemoteManualParamEdits(projectRoot, fileName) {
|
|
35356
|
+
const manifest = readManifest(projectRoot);
|
|
35357
|
+
if (!manifest || !hasRemoteProject(manifest)) return null;
|
|
35358
|
+
const res = await authenticatedFetch(`/api/projects/${manifest.projectId}/manual-param-edits?file=${encodeURIComponent(fileName)}`);
|
|
35359
|
+
if (res.status === 404) return null;
|
|
35360
|
+
if (!res.ok) {
|
|
35361
|
+
const body = await res.json().catch(() => ({}));
|
|
35362
|
+
throw new Error(body.error ?? `Failed to read remote manual edits (${res.status})`);
|
|
35363
|
+
}
|
|
35364
|
+
const data = await res.json();
|
|
35365
|
+
return parseManualParamEditsPayload(data.context);
|
|
35366
|
+
}
|
|
35367
|
+
async function loadManualParamEdits(filePath) {
|
|
35368
|
+
const { projectRoot, fileName } = resolveProjectFile3(filePath);
|
|
35369
|
+
const local = readLocalManualParamEdits(projectRoot, fileName);
|
|
35370
|
+
if (local) return { projectRoot, filePath: fileName, context: local, source: "local" };
|
|
35371
|
+
try {
|
|
35372
|
+
const remote = await readRemoteManualParamEdits(projectRoot, fileName);
|
|
35373
|
+
if (remote) return { projectRoot, filePath: fileName, context: remote, source: "remote" };
|
|
35374
|
+
return { projectRoot, filePath: fileName, context: null };
|
|
35375
|
+
} catch (error) {
|
|
35376
|
+
return {
|
|
35377
|
+
projectRoot,
|
|
35378
|
+
filePath: fileName,
|
|
35379
|
+
context: null,
|
|
35380
|
+
remoteError: error instanceof Error ? error.message : String(error)
|
|
35381
|
+
};
|
|
35382
|
+
}
|
|
35383
|
+
}
|
|
35384
|
+
function statusForLoaded2(loaded) {
|
|
35385
|
+
return {
|
|
35386
|
+
filePath: loaded.filePath,
|
|
35387
|
+
exists: loaded.context !== null,
|
|
35388
|
+
revision: loaded.context?.revision ?? null,
|
|
35389
|
+
submittedAt: loaded.context?.submittedAt ?? null,
|
|
35390
|
+
manualParamCount: loaded.context?.manualParams.length ?? 0,
|
|
35391
|
+
overrideCount: loaded.context ? Object.keys(loaded.context.overrides).length : 0,
|
|
35392
|
+
source: loaded.source
|
|
35393
|
+
};
|
|
35394
|
+
}
|
|
35395
|
+
async function runManualEditsGetCli(args = process.argv.slice(2)) {
|
|
35396
|
+
try {
|
|
35397
|
+
const parsed = parseArgs19("get", args);
|
|
35398
|
+
const loaded = await loadManualParamEdits(parsed.filePath);
|
|
35399
|
+
if (!loaded.context) {
|
|
35400
|
+
console.error(`No submitted manual edits for ${loaded.filePath}.`);
|
|
35401
|
+
if (loaded.remoteError) console.error(`Remote lookup failed: ${loaded.remoteError}`);
|
|
35402
|
+
process.exit(1);
|
|
35403
|
+
}
|
|
35404
|
+
if (parsed.format === "json") {
|
|
35405
|
+
console.log(JSON.stringify({ context: loaded.context, source: loaded.source }, null, 2));
|
|
35406
|
+
return;
|
|
35407
|
+
}
|
|
35408
|
+
console.log(formatManualParamEditsText(loaded.context, loaded.source));
|
|
35409
|
+
} catch (error) {
|
|
35410
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
35411
|
+
process.exit(1);
|
|
35412
|
+
}
|
|
35413
|
+
}
|
|
35414
|
+
async function runManualEditsStatusCli(args = process.argv.slice(2)) {
|
|
35415
|
+
try {
|
|
35416
|
+
const parsed = parseArgs19("status", args);
|
|
35417
|
+
const loaded = await loadManualParamEdits(parsed.filePath);
|
|
35418
|
+
const status = statusForLoaded2(loaded);
|
|
35419
|
+
if (parsed.format === "json") {
|
|
35420
|
+
console.log(JSON.stringify({ status, remoteError: loaded.remoteError }, null, 2));
|
|
35421
|
+
return;
|
|
35422
|
+
}
|
|
35423
|
+
if (!loaded.context) {
|
|
35424
|
+
console.log(`${loaded.filePath}: no submitted manual edits`);
|
|
35425
|
+
if (loaded.remoteError) console.log(`remote lookup failed: ${loaded.remoteError}`);
|
|
35426
|
+
return;
|
|
35427
|
+
}
|
|
35428
|
+
console.log(
|
|
35429
|
+
`${loaded.filePath}: rev ${loaded.context.revision}, ${loaded.context.manualParams.length} manual control${loaded.context.manualParams.length === 1 ? "" : "s"}, ${Object.keys(loaded.context.overrides).length} changed override key${Object.keys(loaded.context.overrides).length === 1 ? "" : "s"}, submitted ${loaded.context.submittedAt}${loaded.source ? ` (${loaded.source})` : ""}`
|
|
35430
|
+
);
|
|
35431
|
+
} catch (error) {
|
|
35432
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
35433
|
+
process.exit(1);
|
|
35434
|
+
}
|
|
35435
|
+
}
|
|
35436
|
+
|
|
34948
35437
|
// cli/forge-svg.ts
|
|
34949
35438
|
import { readFile as readFile5, writeFile as writeFile9 } from "fs/promises";
|
|
34950
35439
|
import { basename as basename17, resolve as resolve42 } from "path";
|
|
34951
|
-
function
|
|
35440
|
+
function usage23() {
|
|
34952
35441
|
console.error("Usage: forgecad export svg <script.forge.js> [input ...] [--output path] [--param Key=Value]");
|
|
34953
35442
|
process.exit(1);
|
|
34954
35443
|
}
|
|
34955
35444
|
function defaultSvgOutput(scriptPath) {
|
|
34956
35445
|
return scriptPath.replace(/\.(forge|sketch)\.js$/, ".svg").replace(/\.js$/, ".svg");
|
|
34957
35446
|
}
|
|
34958
|
-
function
|
|
35447
|
+
function parseArgs20(argv) {
|
|
34959
35448
|
const inputPaths = [];
|
|
34960
35449
|
let outputPath;
|
|
34961
35450
|
const paramOverrides = {};
|
|
@@ -34985,11 +35474,11 @@ function parseArgs19(argv) {
|
|
|
34985
35474
|
async function runSvgCli(argv = process.argv.slice(2)) {
|
|
34986
35475
|
let parsed;
|
|
34987
35476
|
try {
|
|
34988
|
-
parsed =
|
|
35477
|
+
parsed = parseArgs20(argv);
|
|
34989
35478
|
requireExistingInputPaths(parsed.inputPaths);
|
|
34990
35479
|
} catch (error) {
|
|
34991
35480
|
console.error(error instanceof Error ? error.message : String(error));
|
|
34992
|
-
|
|
35481
|
+
usage23();
|
|
34993
35482
|
}
|
|
34994
35483
|
let failures = 0;
|
|
34995
35484
|
for (const [index, scriptPath] of parsed.inputPaths.entries()) {
|
|
@@ -35446,7 +35935,7 @@ function buildUrdfRobotPackage(spec) {
|
|
|
35446
35935
|
}
|
|
35447
35936
|
|
|
35448
35937
|
// cli/forge-urdf.ts
|
|
35449
|
-
function
|
|
35938
|
+
function parseArgs21(argv) {
|
|
35450
35939
|
const inputPaths = [];
|
|
35451
35940
|
let outputPath;
|
|
35452
35941
|
const paramOverrides = {};
|
|
@@ -35498,7 +35987,7 @@ function resolveSimulationModel4(result, jointOverrides) {
|
|
|
35498
35987
|
return collectSimulationModel(def, { state: jointOverrides });
|
|
35499
35988
|
}
|
|
35500
35989
|
async function runUrdfCli(argv = process.argv.slice(2)) {
|
|
35501
|
-
const { inputPaths, outputPath, paramOverrides, jointOverrides } =
|
|
35990
|
+
const { inputPaths, outputPath, paramOverrides, jointOverrides } = parseArgs21(argv);
|
|
35502
35991
|
requireExistingInputPaths(inputPaths);
|
|
35503
35992
|
let failures = 0;
|
|
35504
35993
|
for (const [index, scriptPath] of inputPaths.entries()) {
|
|
@@ -36189,7 +36678,7 @@ ${jointResults.map((result) => result.text).join("\n\n")}
|
|
|
36189
36678
|
}
|
|
36190
36679
|
|
|
36191
36680
|
// cli/forge-usd.ts
|
|
36192
|
-
function
|
|
36681
|
+
function parseArgs22(argv) {
|
|
36193
36682
|
const inputPaths = [];
|
|
36194
36683
|
let outputPath;
|
|
36195
36684
|
const paramOverrides = {};
|
|
@@ -36239,7 +36728,7 @@ function resolveSimulationModel5(result, jointOverrides) {
|
|
|
36239
36728
|
return collectSimulationModel(def, { state: jointOverrides });
|
|
36240
36729
|
}
|
|
36241
36730
|
async function runUsdCli(argv = process.argv.slice(2)) {
|
|
36242
|
-
const { inputPaths, outputPath, paramOverrides, jointOverrides } =
|
|
36731
|
+
const { inputPaths, outputPath, paramOverrides, jointOverrides } = parseArgs22(argv);
|
|
36243
36732
|
requireExistingInputPaths(inputPaths);
|
|
36244
36733
|
let failures = 0;
|
|
36245
36734
|
for (const [index, scriptPath] of inputPaths.entries()) {
|
|
@@ -36357,7 +36846,7 @@ ${url}`);
|
|
|
36357
36846
|
|
|
36358
36847
|
// cli/forge-publish.ts
|
|
36359
36848
|
import { execSync as execSync4 } from "child_process";
|
|
36360
|
-
import { existsSync as
|
|
36849
|
+
import { existsSync as existsSync24 } from "fs";
|
|
36361
36850
|
import { dirname as dirname12, relative as relative6, resolve as resolve45 } from "path";
|
|
36362
36851
|
function findProjectRoot3(dir) {
|
|
36363
36852
|
let current = resolve45(dir);
|
|
@@ -36434,7 +36923,7 @@ async function runPublishCli(args) {
|
|
|
36434
36923
|
process.exit(1);
|
|
36435
36924
|
}
|
|
36436
36925
|
const resolved = resolve45(filePath);
|
|
36437
|
-
if (!
|
|
36926
|
+
if (!existsSync24(resolved)) {
|
|
36438
36927
|
console.error(`File not found: ${filePath}`);
|
|
36439
36928
|
process.exit(1);
|
|
36440
36929
|
}
|
|
@@ -36446,6 +36935,11 @@ async function runPublishCli(args) {
|
|
|
36446
36935
|
console.error("or `cd` into an existing project directory.");
|
|
36447
36936
|
process.exit(1);
|
|
36448
36937
|
}
|
|
36938
|
+
if (!hasRemoteProject(project.manifest)) {
|
|
36939
|
+
console.error("This project has not been created on the server yet.");
|
|
36940
|
+
console.error(" Run `forgecad project push` first, then publish the file.");
|
|
36941
|
+
process.exit(1);
|
|
36942
|
+
}
|
|
36449
36943
|
const relPath = relative6(project.root, resolved).replace(/\\/g, "/");
|
|
36450
36944
|
if (!skipSync) {
|
|
36451
36945
|
console.log(`Project "${project.manifest.slug}" \u2014 syncing...`);
|
|
@@ -36485,16 +36979,16 @@ async function runPublishCli(args) {
|
|
|
36485
36979
|
|
|
36486
36980
|
// cli/doctor-dependencies.ts
|
|
36487
36981
|
import { execFileSync as execFileSync3, execSync as execSync5 } from "child_process";
|
|
36488
|
-
import { existsSync as
|
|
36982
|
+
import { existsSync as existsSync25 } from "fs";
|
|
36489
36983
|
function findExecutablePath2(staticCandidates, binCandidates) {
|
|
36490
36984
|
for (const candidate of staticCandidates) {
|
|
36491
|
-
if (
|
|
36985
|
+
if (existsSync25(candidate)) return candidate;
|
|
36492
36986
|
}
|
|
36493
36987
|
for (const bin of binCandidates) {
|
|
36494
36988
|
try {
|
|
36495
36989
|
const cmd = process.platform === "win32" ? `where ${bin}` : `command -v ${bin}`;
|
|
36496
36990
|
const found = execSync5(cmd, { stdio: ["ignore", "pipe", "ignore"] }).toString().trim().split(/\r?\n/)[0];
|
|
36497
|
-
if (found &&
|
|
36991
|
+
if (found && existsSync25(found)) return found;
|
|
36498
36992
|
} catch {
|
|
36499
36993
|
}
|
|
36500
36994
|
}
|
|
@@ -36505,13 +36999,13 @@ function findExecutableWithEnv(envName, staticCandidates, binCandidates) {
|
|
|
36505
36999
|
if (!configured) return { path: findExecutablePath2(staticCandidates, binCandidates) };
|
|
36506
37000
|
const looksLikePath2 = configured.includes("/") || configured.includes("\\");
|
|
36507
37001
|
if (looksLikePath2) {
|
|
36508
|
-
return
|
|
37002
|
+
return existsSync25(configured) ? { path: configured } : { path: null, note: `${envName}=${configured} was not found` };
|
|
36509
37003
|
}
|
|
36510
37004
|
const found = findExecutablePath2([], [configured]);
|
|
36511
37005
|
return found ? { path: found } : { path: null, note: `${envName}=${configured} was not found on PATH` };
|
|
36512
37006
|
}
|
|
36513
37007
|
function findChrome() {
|
|
36514
|
-
if (process.env.CHROME_PATH &&
|
|
37008
|
+
if (process.env.CHROME_PATH && existsSync25(process.env.CHROME_PATH)) return process.env.CHROME_PATH;
|
|
36515
37009
|
const candidatesByPlatform = {
|
|
36516
37010
|
darwin: [
|
|
36517
37011
|
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
@@ -36545,7 +37039,7 @@ function findChrome() {
|
|
|
36545
37039
|
]);
|
|
36546
37040
|
}
|
|
36547
37041
|
function findFfmpeg() {
|
|
36548
|
-
if (process.env.FFMPEG_PATH &&
|
|
37042
|
+
if (process.env.FFMPEG_PATH && existsSync25(process.env.FFMPEG_PATH)) return process.env.FFMPEG_PATH;
|
|
36549
37043
|
const candidatesByPlatform = {
|
|
36550
37044
|
darwin: ["/opt/homebrew/bin/ffmpeg", "/usr/local/bin/ffmpeg"],
|
|
36551
37045
|
linux: ["/usr/bin/ffmpeg", "/snap/bin/ffmpeg"],
|
|
@@ -36584,8 +37078,8 @@ function getVersion(executable, args) {
|
|
|
36584
37078
|
return void 0;
|
|
36585
37079
|
}
|
|
36586
37080
|
}
|
|
36587
|
-
function okCheck(name,
|
|
36588
|
-
return { name, status: "ok", path:
|
|
37081
|
+
function okCheck(name, path7, version, usedBy) {
|
|
37082
|
+
return { name, status: "ok", path: path7, version, usedBy };
|
|
36589
37083
|
}
|
|
36590
37084
|
function warnCheck(name, note, usedBy) {
|
|
36591
37085
|
return { name, status: "warn", note, usedBy };
|
|
@@ -36724,12 +37218,12 @@ async function recordCliCommandEvent(input) {
|
|
|
36724
37218
|
}
|
|
36725
37219
|
|
|
36726
37220
|
// cli/forge-project.ts
|
|
36727
|
-
import { existsSync as
|
|
36728
|
-
import { createInterface as
|
|
37221
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync14, readFileSync as readFileSync30, writeFileSync as writeFileSync22 } from "fs";
|
|
37222
|
+
import { createInterface as createInterface3 } from "readline";
|
|
36729
37223
|
import { basename as basename18, join as join22, resolve as resolve46 } from "path";
|
|
36730
37224
|
|
|
36731
37225
|
// cli/license.ts
|
|
36732
|
-
import { existsSync as
|
|
37226
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync13, readFileSync as readFileSync29, unlinkSync as unlinkSync2, writeFileSync as writeFileSync21 } from "fs";
|
|
36733
37227
|
import { homedir as homedir8 } from "os";
|
|
36734
37228
|
import { join as join21 } from "path";
|
|
36735
37229
|
var VALID_TIERS = /* @__PURE__ */ new Set(["free", "pro", "team"]);
|
|
@@ -36780,16 +37274,16 @@ function licenseDir() {
|
|
|
36780
37274
|
}
|
|
36781
37275
|
function ensureLicenseDir() {
|
|
36782
37276
|
const dir = licenseDir();
|
|
36783
|
-
if (!
|
|
37277
|
+
if (!existsSync26(dir)) mkdirSync13(dir, { recursive: true });
|
|
36784
37278
|
}
|
|
36785
37279
|
function licenseFilePath() {
|
|
36786
37280
|
return join21(licenseDir(), "license.json");
|
|
36787
37281
|
}
|
|
36788
37282
|
function readStoredLicense() {
|
|
36789
|
-
const
|
|
36790
|
-
if (!
|
|
37283
|
+
const path7 = licenseFilePath();
|
|
37284
|
+
if (!existsSync26(path7)) return null;
|
|
36791
37285
|
try {
|
|
36792
|
-
return JSON.parse(readFileSync29(
|
|
37286
|
+
return JSON.parse(readFileSync29(path7, "utf-8"));
|
|
36793
37287
|
} catch {
|
|
36794
37288
|
return null;
|
|
36795
37289
|
}
|
|
@@ -36882,8 +37376,8 @@ function storeLicenseFromToken(token) {
|
|
|
36882
37376
|
};
|
|
36883
37377
|
}
|
|
36884
37378
|
function deactivateLicense() {
|
|
36885
|
-
const
|
|
36886
|
-
if (
|
|
37379
|
+
const path7 = licenseFilePath();
|
|
37380
|
+
if (existsSync26(path7)) unlinkSync2(path7);
|
|
36887
37381
|
}
|
|
36888
37382
|
function tryBackgroundRefresh() {
|
|
36889
37383
|
const stored = readStoredLicense();
|
|
@@ -36965,11 +37459,69 @@ function printProductionOutputNotice(commandPath) {
|
|
|
36965
37459
|
function slugify(name) {
|
|
36966
37460
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
36967
37461
|
}
|
|
37462
|
+
var PROJECT_VISIBILITIES = /* @__PURE__ */ new Set(["private", "shared", "public"]);
|
|
37463
|
+
function projectInitUsage() {
|
|
37464
|
+
return "Usage: forgecad project init [name] [--slug <slug>] [--visibility <private|shared|public>]";
|
|
37465
|
+
}
|
|
37466
|
+
function parseProjectInitArgs(args, cwd) {
|
|
37467
|
+
let name;
|
|
37468
|
+
let slug2;
|
|
37469
|
+
let visibility = "private";
|
|
37470
|
+
for (let i = 0; i < args.length; i++) {
|
|
37471
|
+
const arg = args[i];
|
|
37472
|
+
if (arg === "--slug") {
|
|
37473
|
+
const value = args[++i];
|
|
37474
|
+
if (!value || value.startsWith("-")) throw new Error(projectInitUsage());
|
|
37475
|
+
slug2 = value;
|
|
37476
|
+
} else if (arg.startsWith("--slug=")) {
|
|
37477
|
+
slug2 = arg.slice("--slug=".length);
|
|
37478
|
+
if (!slug2) throw new Error(projectInitUsage());
|
|
37479
|
+
} else if (arg === "--visibility") {
|
|
37480
|
+
const value = args[++i];
|
|
37481
|
+
if (!value || value.startsWith("-")) throw new Error(projectInitUsage());
|
|
37482
|
+
visibility = value;
|
|
37483
|
+
} else if (arg.startsWith("--visibility=")) {
|
|
37484
|
+
visibility = arg.slice("--visibility=".length);
|
|
37485
|
+
if (!visibility) throw new Error(projectInitUsage());
|
|
37486
|
+
} else if (arg.startsWith("-")) {
|
|
37487
|
+
throw new Error(`Unknown project init option: ${arg}
|
|
37488
|
+
${projectInitUsage()}`);
|
|
37489
|
+
} else if (!name) {
|
|
37490
|
+
name = arg;
|
|
37491
|
+
} else {
|
|
37492
|
+
throw new Error(`Unexpected project init argument: ${arg}
|
|
37493
|
+
${projectInitUsage()}`);
|
|
37494
|
+
}
|
|
37495
|
+
}
|
|
37496
|
+
name = (name ?? basename18(cwd)).trim();
|
|
37497
|
+
slug2 = (slug2 ?? slugify(name)).trim();
|
|
37498
|
+
if (!name) throw new Error("Project name cannot be empty.");
|
|
37499
|
+
if (!slug2) throw new Error("Project slug cannot be empty. Pass --slug with a URL-safe name.");
|
|
37500
|
+
if (!PROJECT_VISIBILITIES.has(visibility)) {
|
|
37501
|
+
throw new Error(`Invalid project visibility "${visibility}". Expected private, shared, or public.`);
|
|
37502
|
+
}
|
|
37503
|
+
return { name, slug: slug2, visibility };
|
|
37504
|
+
}
|
|
37505
|
+
function localProjectName(cwd, manifest) {
|
|
37506
|
+
return (manifest.name ?? manifest.slug ?? basename18(cwd)).trim();
|
|
37507
|
+
}
|
|
37508
|
+
function localProjectSlug(cwd, manifest) {
|
|
37509
|
+
const slug2 = (manifest.slug ?? slugify(localProjectName(cwd, manifest))).trim();
|
|
37510
|
+
if (!slug2) throw new Error("Project slug cannot be empty. Re-run `forgecad project init` with --slug.");
|
|
37511
|
+
return slug2;
|
|
37512
|
+
}
|
|
37513
|
+
function localProjectVisibility(manifest) {
|
|
37514
|
+
const visibility = manifest.visibility ?? "private";
|
|
37515
|
+
if (!PROJECT_VISIBILITIES.has(visibility)) {
|
|
37516
|
+
throw new Error(`Invalid project visibility "${visibility}" in forgecad.json. Expected private, shared, or public.`);
|
|
37517
|
+
}
|
|
37518
|
+
return visibility;
|
|
37519
|
+
}
|
|
36968
37520
|
function padRight(s, w) {
|
|
36969
37521
|
return s.length >= w ? s : s + " ".repeat(w - s.length);
|
|
36970
37522
|
}
|
|
36971
|
-
function
|
|
36972
|
-
const rl =
|
|
37523
|
+
function confirm2(question) {
|
|
37524
|
+
const rl = createInterface3({ input: process.stdin, output: process.stderr });
|
|
36973
37525
|
return new Promise((resolve54) => {
|
|
36974
37526
|
rl.question(`${question} [y/N] `, (answer) => {
|
|
36975
37527
|
rl.close();
|
|
@@ -36982,7 +37534,7 @@ function moveCandidateReasonLabel(candidate) {
|
|
|
36982
37534
|
return candidate.reason === "same-content" ? "same content" : "same filename";
|
|
36983
37535
|
}
|
|
36984
37536
|
function promptChoice(question) {
|
|
36985
|
-
const rl =
|
|
37537
|
+
const rl = createInterface3({ input: process.stdin, output: process.stderr });
|
|
36986
37538
|
return new Promise((resolve54) => {
|
|
36987
37539
|
rl.question(question, (answer) => {
|
|
36988
37540
|
rl.close();
|
|
@@ -37099,44 +37651,19 @@ async function runProjectInitCli(args) {
|
|
|
37099
37651
|
console.error(`Already a ForgeCAD project (slug: ${existing.slug}).`);
|
|
37100
37652
|
process.exit(1);
|
|
37101
37653
|
}
|
|
37102
|
-
requireCliAuth();
|
|
37103
|
-
let name;
|
|
37104
|
-
let slug2;
|
|
37105
|
-
let visibility = "private";
|
|
37106
|
-
for (let i = 0; i < args.length; i++) {
|
|
37107
|
-
if (args[i] === "--slug" && args[i + 1]) {
|
|
37108
|
-
slug2 = args[++i];
|
|
37109
|
-
} else if (args[i] === "--visibility" && args[i + 1]) {
|
|
37110
|
-
visibility = args[++i];
|
|
37111
|
-
} else if (!args[i].startsWith("-")) {
|
|
37112
|
-
name = args[i];
|
|
37113
|
-
}
|
|
37114
|
-
}
|
|
37115
|
-
name = name ?? basename18(cwd);
|
|
37116
|
-
slug2 = slug2 ?? slugify(name);
|
|
37117
|
-
console.log(`Creating project "${name}" (${slug2})...`);
|
|
37118
37654
|
try {
|
|
37119
|
-
const
|
|
37655
|
+
const { name, slug: slug2, visibility } = parseProjectInitArgs(args, cwd);
|
|
37120
37656
|
const manifest = {
|
|
37121
|
-
|
|
37122
|
-
slug:
|
|
37123
|
-
server: getServerUrl()
|
|
37657
|
+
name,
|
|
37658
|
+
slug: slug2,
|
|
37659
|
+
server: getServerUrl(),
|
|
37660
|
+
visibility
|
|
37124
37661
|
};
|
|
37125
37662
|
writeManifest(cwd, manifest);
|
|
37126
|
-
|
|
37127
|
-
|
|
37128
|
-
|
|
37129
|
-
|
|
37130
|
-
await saveRemoteFile(project.id, file.path, file.content);
|
|
37131
|
-
console.log(` ${file.path}`);
|
|
37132
|
-
}
|
|
37133
|
-
}
|
|
37134
|
-
const fileIds = await fetchFileIds(project.id);
|
|
37135
|
-
writeManifest(cwd, { ...manifest, files: fileIds });
|
|
37136
|
-
console.log(`\x1B[32mProject initialized.\x1B[0m`);
|
|
37137
|
-
console.log(` ID: ${project.id}`);
|
|
37138
|
-
console.log(` Slug: ${project.slug}`);
|
|
37139
|
-
console.log(` URL: ${getServerUrl()}/app/p/${project.id}`);
|
|
37663
|
+
console.log(`\x1B[32mProject initialized locally.\x1B[0m`);
|
|
37664
|
+
console.log(` Name: ${name}`);
|
|
37665
|
+
console.log(` Slug: ${slug2}`);
|
|
37666
|
+
console.log(` Next: forgecad project push`);
|
|
37140
37667
|
} catch (error) {
|
|
37141
37668
|
console.error(`\x1B[31m${error instanceof Error ? error.message : "Init failed"}\x1B[0m`);
|
|
37142
37669
|
process.exit(1);
|
|
@@ -37158,7 +37685,7 @@ async function runProjectCloneCli(args) {
|
|
|
37158
37685
|
throw new Error(`Project "${slug2}" not found. Run \`forgecad project list\` to see available projects.`);
|
|
37159
37686
|
}
|
|
37160
37687
|
const targetDir = resolve46(slug2);
|
|
37161
|
-
if (
|
|
37688
|
+
if (existsSync27(targetDir)) {
|
|
37162
37689
|
throw new Error(`Directory "${slug2}" already exists.`);
|
|
37163
37690
|
}
|
|
37164
37691
|
mkdirSync14(targetDir, { recursive: true });
|
|
@@ -37187,7 +37714,7 @@ async function runProjectCloneCli(args) {
|
|
|
37187
37714
|
}
|
|
37188
37715
|
async function runProjectPullCli(args) {
|
|
37189
37716
|
const cwd = process.cwd();
|
|
37190
|
-
let manifest =
|
|
37717
|
+
let manifest = requireRemoteManifest(cwd, "pull remote changes");
|
|
37191
37718
|
requireCliAuth();
|
|
37192
37719
|
const force = args.includes("--force") || args.includes("-f");
|
|
37193
37720
|
if (!manifest.files) {
|
|
@@ -37224,7 +37751,7 @@ async function runProjectPullCli(args) {
|
|
|
37224
37751
|
console.log(`
|
|
37225
37752
|
${incoming.length} file(s) will be overwritten locally.`);
|
|
37226
37753
|
if (!force) {
|
|
37227
|
-
const ok = await
|
|
37754
|
+
const ok = await confirm2("Proceed?");
|
|
37228
37755
|
if (!ok) {
|
|
37229
37756
|
console.log("Aborted.");
|
|
37230
37757
|
return;
|
|
@@ -37254,15 +37781,61 @@ async function runProjectPushCli(args) {
|
|
|
37254
37781
|
requireCliAuth();
|
|
37255
37782
|
const force = args.includes("--force") || args.includes("-f");
|
|
37256
37783
|
const deleteMissing = args.includes("--delete-missing");
|
|
37257
|
-
if (!manifest.files) {
|
|
37258
|
-
console.log("Upgrading manifest to track file IDs...");
|
|
37259
|
-
const fileIds = await fetchFileIds(manifest.projectId);
|
|
37260
|
-
manifest = { ...manifest, files: fileIds };
|
|
37261
|
-
writeManifest(cwd, manifest);
|
|
37262
|
-
}
|
|
37263
|
-
console.log(`Pushing to "${manifest.slug}"...`);
|
|
37264
37784
|
try {
|
|
37265
37785
|
const localFiles = scanLocalFiles(cwd);
|
|
37786
|
+
if (!hasRemoteProject(manifest)) {
|
|
37787
|
+
const name = localProjectName(cwd, manifest);
|
|
37788
|
+
const slug2 = localProjectSlug(cwd, manifest);
|
|
37789
|
+
const visibility = localProjectVisibility(manifest);
|
|
37790
|
+
console.log(`Project "${name}" (${slug2}) has no hosted project yet.`);
|
|
37791
|
+
if (localFiles.length > 0) {
|
|
37792
|
+
for (const file of localFiles) {
|
|
37793
|
+
console.log(` \x1B[32m+ add \x1B[0m ${file.path}`);
|
|
37794
|
+
}
|
|
37795
|
+
console.log(` ${localFiles.length} file(s) will be uploaded.`);
|
|
37796
|
+
} else {
|
|
37797
|
+
console.log(" No local project files found; the hosted project will start empty.");
|
|
37798
|
+
}
|
|
37799
|
+
if (!force) {
|
|
37800
|
+
const ok = await confirm2(`Create hosted project "${name}" and push now?`);
|
|
37801
|
+
if (!ok) {
|
|
37802
|
+
console.log("Aborted.");
|
|
37803
|
+
return;
|
|
37804
|
+
}
|
|
37805
|
+
}
|
|
37806
|
+
console.log(`Creating hosted project "${name}" (${slug2})...`);
|
|
37807
|
+
const project = await createProject(slug2, name, visibility);
|
|
37808
|
+
manifest = {
|
|
37809
|
+
...manifest,
|
|
37810
|
+
projectId: project.id,
|
|
37811
|
+
name,
|
|
37812
|
+
slug: project.slug,
|
|
37813
|
+
server: getServerUrl(),
|
|
37814
|
+
visibility
|
|
37815
|
+
};
|
|
37816
|
+
writeManifest(cwd, manifest);
|
|
37817
|
+
if (localFiles.length > 0) {
|
|
37818
|
+
console.log(`Pushing ${localFiles.length} file(s)...`);
|
|
37819
|
+
for (const file of localFiles) {
|
|
37820
|
+
await saveRemoteFile(project.id, file.path, file.content);
|
|
37821
|
+
console.log(` ${file.path}`);
|
|
37822
|
+
}
|
|
37823
|
+
}
|
|
37824
|
+
const fileIds2 = await fetchFileIds(project.id);
|
|
37825
|
+
writeManifest(cwd, { ...manifest, files: fileIds2 });
|
|
37826
|
+
console.log(`\x1B[32mProject pushed.\x1B[0m`);
|
|
37827
|
+
console.log(` ID: ${project.id}`);
|
|
37828
|
+
console.log(` Slug: ${project.slug}`);
|
|
37829
|
+
console.log(` URL: ${getServerUrl()}/app/p/${project.id}`);
|
|
37830
|
+
return;
|
|
37831
|
+
}
|
|
37832
|
+
if (!manifest.files) {
|
|
37833
|
+
console.log("Upgrading manifest to track file IDs...");
|
|
37834
|
+
const fileIds2 = await fetchFileIds(manifest.projectId);
|
|
37835
|
+
manifest = { ...manifest, files: fileIds2 };
|
|
37836
|
+
writeManifest(cwd, manifest);
|
|
37837
|
+
}
|
|
37838
|
+
console.log(`Pushing to "${manifest.slug}"...`);
|
|
37266
37839
|
let remoteFiles = await fetchRemoteFiles(manifest.projectId);
|
|
37267
37840
|
let movedCount = 0;
|
|
37268
37841
|
if (!deleteMissing && manifest.files && Object.keys(manifest.files).length > 0) {
|
|
@@ -37279,8 +37852,8 @@ Checking ${orphaned.length} missing tracked file(s) against ${untracked.length}
|
|
|
37279
37852
|
}
|
|
37280
37853
|
const remainingUntracked = [...untracked].sort((a, b) => a.path.localeCompare(b.path));
|
|
37281
37854
|
const skippedMovePrompts = [];
|
|
37282
|
-
const removeRemainingUntracked = (
|
|
37283
|
-
const index = remainingUntracked.findIndex((file) => file.path ===
|
|
37855
|
+
const removeRemainingUntracked = (path7) => {
|
|
37856
|
+
const index = remainingUntracked.findIndex((file) => file.path === path7);
|
|
37284
37857
|
if (index >= 0) remainingUntracked.splice(index, 1);
|
|
37285
37858
|
};
|
|
37286
37859
|
for (const [fromPath, fileId] of orphaned) {
|
|
@@ -37304,7 +37877,7 @@ Checking ${orphaned.length} missing tracked file(s) against ${untracked.length}
|
|
|
37304
37877
|
}
|
|
37305
37878
|
if (candidates.length === 0 && remainingUntracked.length === 1) {
|
|
37306
37879
|
const candidate = remainingUntracked[0];
|
|
37307
|
-
const yes = await
|
|
37880
|
+
const yes = await confirm2(`Did you move ${fromPath} \u2192 ${candidate.path}?`);
|
|
37308
37881
|
if (yes) {
|
|
37309
37882
|
remoteFiles = await applyProjectPushMove({ cwd, manifest, remoteFiles, fromPath, toPath: candidate.path, fileId });
|
|
37310
37883
|
movedCount++;
|
|
@@ -37322,7 +37895,7 @@ Checking ${orphaned.length} missing tracked file(s) against ${untracked.length}
|
|
|
37322
37895
|
}
|
|
37323
37896
|
if (candidates.length === 1) {
|
|
37324
37897
|
const candidate = candidates[0];
|
|
37325
|
-
const yes = await
|
|
37898
|
+
const yes = await confirm2(`Did you move ${fromPath} \u2192 ${candidate.path} (${moveCandidateReasonLabel(candidate)})?`);
|
|
37326
37899
|
if (yes) {
|
|
37327
37900
|
remoteFiles = await applyProjectPushMove({ cwd, manifest, remoteFiles, fromPath, toPath: candidate.path, fileId });
|
|
37328
37901
|
movedCount++;
|
|
@@ -37360,7 +37933,7 @@ Checking ${orphaned.length} missing tracked file(s) against ${untracked.length}
|
|
|
37360
37933
|
}
|
|
37361
37934
|
} else if (deleteMissing && manifest.files && Object.keys(manifest.files).length > 0) {
|
|
37362
37935
|
const localPathSet = new Set(localFiles.map((f2) => f2.path));
|
|
37363
|
-
const missingTrackedCount = Object.keys(manifest.files).filter((
|
|
37936
|
+
const missingTrackedCount = Object.keys(manifest.files).filter((path7) => !localPathSet.has(path7)).length;
|
|
37364
37937
|
if (missingTrackedCount > 0) {
|
|
37365
37938
|
console.log(`
|
|
37366
37939
|
--delete-missing: skipping move detection for ${missingTrackedCount} missing tracked file(s).`);
|
|
@@ -37399,7 +37972,7 @@ Checking ${orphaned.length} missing tracked file(s) against ${untracked.length}
|
|
|
37399
37972
|
}
|
|
37400
37973
|
console.log(` ${outgoing.length} change(s) total.`);
|
|
37401
37974
|
if (!force) {
|
|
37402
|
-
const ok = await
|
|
37975
|
+
const ok = await confirm2("Push these changes?");
|
|
37403
37976
|
if (!ok) {
|
|
37404
37977
|
console.log("Aborted.");
|
|
37405
37978
|
return;
|
|
@@ -37426,9 +37999,26 @@ Checking ${orphaned.length} missing tracked file(s) against ${untracked.length}
|
|
|
37426
37999
|
async function runProjectStatusCli(_args) {
|
|
37427
38000
|
const cwd = process.cwd();
|
|
37428
38001
|
const manifest = requireManifest(cwd);
|
|
37429
|
-
requireCliAuth();
|
|
37430
38002
|
try {
|
|
37431
38003
|
const localFiles = scanLocalFiles(cwd);
|
|
38004
|
+
if (!hasRemoteProject(manifest)) {
|
|
38005
|
+
console.log(`Project: ${manifest.slug}`);
|
|
38006
|
+
console.log(`Server: ${manifest.server}`);
|
|
38007
|
+
console.log("Remote: not created yet");
|
|
38008
|
+
console.log(`Files: ${localFiles.length} local`);
|
|
38009
|
+
console.log("");
|
|
38010
|
+
if (localFiles.length === 0) {
|
|
38011
|
+
console.log(" Local-only project. Run `forgecad project push` to create the hosted project.");
|
|
38012
|
+
} else {
|
|
38013
|
+
for (const file of localFiles) {
|
|
38014
|
+
console.log(` \x1B[32m+ local \x1B[0m ${file.path}`);
|
|
38015
|
+
}
|
|
38016
|
+
console.log("");
|
|
38017
|
+
console.log(` ${localFiles.length} file(s) will be uploaded when you run \`forgecad project push\`.`);
|
|
38018
|
+
}
|
|
38019
|
+
return;
|
|
38020
|
+
}
|
|
38021
|
+
requireCliAuth();
|
|
37432
38022
|
const remoteFiles = await fetchRemoteFiles(manifest.projectId);
|
|
37433
38023
|
const diffs = diffFiles(
|
|
37434
38024
|
localFiles,
|
|
@@ -37463,7 +38053,7 @@ async function runProjectListCli(_args) {
|
|
|
37463
38053
|
try {
|
|
37464
38054
|
const projects = await listProjects();
|
|
37465
38055
|
if (projects.length === 0) {
|
|
37466
|
-
console.log("No projects.
|
|
38056
|
+
console.log("No hosted projects. Run `forgecad project init` in a project folder, then `forgecad project push`.");
|
|
37467
38057
|
return;
|
|
37468
38058
|
}
|
|
37469
38059
|
const idWidth = Math.max(2, ...projects.map((p) => p.id.length));
|
|
@@ -37512,6 +38102,11 @@ async function runProjectOpenCli(_args) {
|
|
|
37512
38102
|
openBrowser2(url2);
|
|
37513
38103
|
return;
|
|
37514
38104
|
}
|
|
38105
|
+
if (!hasRemoteProject(manifest)) {
|
|
38106
|
+
console.error("This project has not been created on the server yet.");
|
|
38107
|
+
console.error(" Run `forgecad studio .` for local editing, or `forgecad project push` to create the hosted project first.");
|
|
38108
|
+
process.exit(1);
|
|
38109
|
+
}
|
|
37515
38110
|
const server = normalizeServerUrl(manifest.server || readStoredAuth()?.server || "https://forgecad.io", "Project server URL");
|
|
37516
38111
|
const url = `${server}/app?project=${manifest.projectId}`;
|
|
37517
38112
|
console.log(`Opening "${manifest.slug}" in browser...`);
|
|
@@ -37592,7 +38187,7 @@ return assembly('Project Assembly')
|
|
|
37592
38187
|
};
|
|
37593
38188
|
async function runProjectInfoCli(_args) {
|
|
37594
38189
|
const cwd = process.cwd();
|
|
37595
|
-
const manifest =
|
|
38190
|
+
const manifest = requireRemoteManifest(cwd, "show project info");
|
|
37596
38191
|
requireCliAuth();
|
|
37597
38192
|
try {
|
|
37598
38193
|
const project = await getProject(manifest.projectId);
|
|
@@ -37616,7 +38211,7 @@ async function runProjectInfoCli(_args) {
|
|
|
37616
38211
|
}
|
|
37617
38212
|
async function runProjectRenameCli(args) {
|
|
37618
38213
|
const cwd = process.cwd();
|
|
37619
|
-
const manifest =
|
|
38214
|
+
const manifest = requireRemoteManifest(cwd, "rename it");
|
|
37620
38215
|
requireCliAuth();
|
|
37621
38216
|
let name;
|
|
37622
38217
|
let slug2;
|
|
@@ -37645,7 +38240,7 @@ async function runProjectRenameCli(args) {
|
|
|
37645
38240
|
}
|
|
37646
38241
|
async function runProjectSetVisibilityCli(args) {
|
|
37647
38242
|
const cwd = process.cwd();
|
|
37648
|
-
const manifest =
|
|
38243
|
+
const manifest = requireRemoteManifest(cwd, "change its visibility");
|
|
37649
38244
|
requireCliAuth();
|
|
37650
38245
|
const visibility = args.find((a) => !a.startsWith("-"));
|
|
37651
38246
|
if (!visibility || !["private", "shared", "public"].includes(visibility)) {
|
|
@@ -37662,14 +38257,14 @@ async function runProjectSetVisibilityCli(args) {
|
|
|
37662
38257
|
}
|
|
37663
38258
|
async function runProjectDeleteCli(args) {
|
|
37664
38259
|
const cwd = process.cwd();
|
|
37665
|
-
const manifest =
|
|
38260
|
+
const manifest = requireRemoteManifest(cwd, "delete it");
|
|
37666
38261
|
requireCliAuth();
|
|
37667
38262
|
const force = args.includes("--force") || args.includes("-f");
|
|
37668
38263
|
try {
|
|
37669
38264
|
const project = await getProject(manifest.projectId);
|
|
37670
38265
|
if (!force) {
|
|
37671
38266
|
console.log(`\x1B[31mThis will permanently delete project "${project.name}" (${project.slug}) and all its files.\x1B[0m`);
|
|
37672
|
-
const ok = await
|
|
38267
|
+
const ok = await confirm2("Are you sure?");
|
|
37673
38268
|
if (!ok) {
|
|
37674
38269
|
console.log("Aborted.");
|
|
37675
38270
|
return;
|
|
@@ -37684,7 +38279,7 @@ async function runProjectDeleteCli(args) {
|
|
|
37684
38279
|
}
|
|
37685
38280
|
async function runFileListCli(args) {
|
|
37686
38281
|
const cwd = process.cwd();
|
|
37687
|
-
const manifest =
|
|
38282
|
+
const manifest = requireRemoteManifest(cwd, "list remote files");
|
|
37688
38283
|
requireCliAuth();
|
|
37689
38284
|
const filterPath = args.find((a) => !a.startsWith("-"));
|
|
37690
38285
|
try {
|
|
@@ -37710,7 +38305,7 @@ async function runFileListCli(args) {
|
|
|
37710
38305
|
}
|
|
37711
38306
|
async function runFileReadCli(args) {
|
|
37712
38307
|
const cwd = process.cwd();
|
|
37713
|
-
const manifest =
|
|
38308
|
+
const manifest = requireRemoteManifest(cwd, "read remote files");
|
|
37714
38309
|
requireCliAuth();
|
|
37715
38310
|
const filePath = args.find((a) => !a.startsWith("-"));
|
|
37716
38311
|
if (!filePath) {
|
|
@@ -37727,7 +38322,7 @@ async function runFileReadCli(args) {
|
|
|
37727
38322
|
}
|
|
37728
38323
|
async function runFileSaveCli(args) {
|
|
37729
38324
|
const cwd = process.cwd();
|
|
37730
|
-
const manifest =
|
|
38325
|
+
const manifest = requireRemoteManifest(cwd, "save remote files");
|
|
37731
38326
|
requireCliAuth();
|
|
37732
38327
|
let filePath;
|
|
37733
38328
|
let content;
|
|
@@ -37755,7 +38350,7 @@ async function runFileSaveCli(args) {
|
|
|
37755
38350
|
content = Buffer.concat(chunks).toString("utf-8");
|
|
37756
38351
|
} else if (!content) {
|
|
37757
38352
|
const localPath = join22(cwd, filePath);
|
|
37758
|
-
if (!
|
|
38353
|
+
if (!existsSync27(localPath)) {
|
|
37759
38354
|
console.error(`Local file not found: ${filePath}`);
|
|
37760
38355
|
console.error("Use --content or --stdin to provide content directly.");
|
|
37761
38356
|
process.exit(1);
|
|
@@ -37771,7 +38366,7 @@ async function runFileSaveCli(args) {
|
|
|
37771
38366
|
}
|
|
37772
38367
|
async function runFileDeleteCli(args) {
|
|
37773
38368
|
const cwd = process.cwd();
|
|
37774
|
-
const manifest =
|
|
38369
|
+
const manifest = requireRemoteManifest(cwd, "delete remote files");
|
|
37775
38370
|
requireCliAuth();
|
|
37776
38371
|
const force = args.includes("--force") || args.includes("-f");
|
|
37777
38372
|
const filePath = args.find((a) => !a.startsWith("-"));
|
|
@@ -37781,7 +38376,7 @@ async function runFileDeleteCli(args) {
|
|
|
37781
38376
|
}
|
|
37782
38377
|
try {
|
|
37783
38378
|
if (!force) {
|
|
37784
|
-
const ok = await
|
|
38379
|
+
const ok = await confirm2(`Delete remote file "${filePath}"?`);
|
|
37785
38380
|
if (!ok) {
|
|
37786
38381
|
console.log("Aborted.");
|
|
37787
38382
|
return;
|
|
@@ -37796,7 +38391,7 @@ async function runFileDeleteCli(args) {
|
|
|
37796
38391
|
}
|
|
37797
38392
|
async function runFileRenameCli(args) {
|
|
37798
38393
|
const cwd = process.cwd();
|
|
37799
|
-
const manifest =
|
|
38394
|
+
const manifest = requireRemoteManifest(cwd, "rename remote files");
|
|
37800
38395
|
requireCliAuth();
|
|
37801
38396
|
const positionals = args.filter((a) => !a.startsWith("-"));
|
|
37802
38397
|
if (positionals.length < 2) {
|
|
@@ -37816,7 +38411,7 @@ async function runFileRenameCli(args) {
|
|
|
37816
38411
|
}
|
|
37817
38412
|
async function runFileMkdirCli(args) {
|
|
37818
38413
|
const cwd = process.cwd();
|
|
37819
|
-
const manifest =
|
|
38414
|
+
const manifest = requireRemoteManifest(cwd, "create remote folders");
|
|
37820
38415
|
requireCliAuth();
|
|
37821
38416
|
const dirPath = args.find((a) => !a.startsWith("-"));
|
|
37822
38417
|
if (!dirPath) {
|
|
@@ -37833,7 +38428,7 @@ async function runFileMkdirCli(args) {
|
|
|
37833
38428
|
}
|
|
37834
38429
|
async function runFileCopyCli(args) {
|
|
37835
38430
|
const cwd = process.cwd();
|
|
37836
|
-
const manifest =
|
|
38431
|
+
const manifest = requireRemoteManifest(cwd, "copy remote files");
|
|
37837
38432
|
requireCliAuth();
|
|
37838
38433
|
let destFilename;
|
|
37839
38434
|
const positionals = [];
|
|
@@ -37865,7 +38460,7 @@ async function runFileCopyCli(args) {
|
|
|
37865
38460
|
}
|
|
37866
38461
|
async function runProjectMembersCli(_args) {
|
|
37867
38462
|
const cwd = process.cwd();
|
|
37868
|
-
const manifest =
|
|
38463
|
+
const manifest = requireRemoteManifest(cwd, "manage members");
|
|
37869
38464
|
requireCliAuth();
|
|
37870
38465
|
try {
|
|
37871
38466
|
const members = await listMembers(manifest.projectId);
|
|
@@ -37887,7 +38482,7 @@ async function runProjectMembersCli(_args) {
|
|
|
37887
38482
|
}
|
|
37888
38483
|
async function runProjectAddMemberCli(args) {
|
|
37889
38484
|
const cwd = process.cwd();
|
|
37890
|
-
const manifest =
|
|
38485
|
+
const manifest = requireRemoteManifest(cwd, "add members");
|
|
37891
38486
|
requireCliAuth();
|
|
37892
38487
|
let email;
|
|
37893
38488
|
let role = "editor";
|
|
@@ -37916,7 +38511,7 @@ async function runProjectAddMemberCli(args) {
|
|
|
37916
38511
|
}
|
|
37917
38512
|
async function runProjectRemoveMemberCli(args) {
|
|
37918
38513
|
const cwd = process.cwd();
|
|
37919
|
-
const manifest =
|
|
38514
|
+
const manifest = requireRemoteManifest(cwd, "remove members");
|
|
37920
38515
|
requireCliAuth();
|
|
37921
38516
|
const email = args.find((a) => !a.startsWith("-"));
|
|
37922
38517
|
if (!email) {
|
|
@@ -37938,7 +38533,7 @@ async function runProjectRemoveMemberCli(args) {
|
|
|
37938
38533
|
}
|
|
37939
38534
|
async function runProjectSetRoleCli(args) {
|
|
37940
38535
|
const cwd = process.cwd();
|
|
37941
|
-
const manifest =
|
|
38536
|
+
const manifest = requireRemoteManifest(cwd, "change member roles");
|
|
37942
38537
|
requireCliAuth();
|
|
37943
38538
|
const positionals = args.filter((a) => !a.startsWith("-"));
|
|
37944
38539
|
if (positionals.length < 2) {
|
|
@@ -37997,7 +38592,7 @@ async function runSharesDeleteCli(args) {
|
|
|
37997
38592
|
}
|
|
37998
38593
|
try {
|
|
37999
38594
|
if (!force) {
|
|
38000
|
-
const ok = await
|
|
38595
|
+
const ok = await confirm2(`Unpublish share "${shareId}"?`);
|
|
38001
38596
|
if (!ok) {
|
|
38002
38597
|
console.log("Aborted.");
|
|
38003
38598
|
return;
|
|
@@ -38028,7 +38623,7 @@ async function runNewCli(args) {
|
|
|
38028
38623
|
}
|
|
38029
38624
|
name = name ?? `my-${template}`;
|
|
38030
38625
|
const filename = name.endsWith(tmpl.ext) ? name : `${name}${tmpl.ext}`;
|
|
38031
|
-
if (
|
|
38626
|
+
if (existsSync27(filename)) {
|
|
38032
38627
|
console.error(`File already exists: ${filename}`);
|
|
38033
38628
|
process.exit(1);
|
|
38034
38629
|
}
|
|
@@ -38122,7 +38717,7 @@ async function runTokenRevokeCli(args) {
|
|
|
38122
38717
|
process.exit(1);
|
|
38123
38718
|
}
|
|
38124
38719
|
if (!force) {
|
|
38125
|
-
const ok = await
|
|
38720
|
+
const ok = await confirm2(`Revoke token "${token.name}"? Any CI/CD pipelines using it will stop working.`);
|
|
38126
38721
|
if (!ok) {
|
|
38127
38722
|
console.log("Cancelled.");
|
|
38128
38723
|
return;
|
|
@@ -38178,13 +38773,13 @@ function findCollisions(entries) {
|
|
|
38178
38773
|
}
|
|
38179
38774
|
return collisions;
|
|
38180
38775
|
}
|
|
38181
|
-
function
|
|
38776
|
+
function usage24() {
|
|
38182
38777
|
console.error("Usage: forgecad check params <script.forge.js> [--samples N]");
|
|
38183
38778
|
process.exit(1);
|
|
38184
38779
|
}
|
|
38185
38780
|
async function runParamCheckCli(argv = process.argv.slice(2)) {
|
|
38186
38781
|
const scriptPath = argv[0];
|
|
38187
|
-
if (!scriptPath)
|
|
38782
|
+
if (!scriptPath) usage24();
|
|
38188
38783
|
const samplesArg = argv.indexOf("--samples");
|
|
38189
38784
|
const numSamples = samplesArg >= 0 ? parseInt(argv[samplesArg + 1], 10) : 8;
|
|
38190
38785
|
const code = readFileSync31(resolve47(scriptPath), "utf-8");
|
|
@@ -38868,7 +39463,7 @@ function buildPrintCheckReport(objects, verifications, options) {
|
|
|
38868
39463
|
}
|
|
38869
39464
|
|
|
38870
39465
|
// cli/print-check.ts
|
|
38871
|
-
function
|
|
39466
|
+
function usage25() {
|
|
38872
39467
|
console.error(
|
|
38873
39468
|
"Usage: forgecad check print <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [input ...] [--json] [--output report.json] [--profile fdm-pla-0.4mm] [--param Key=Value] [--joint JointName=Value]"
|
|
38874
39469
|
);
|
|
@@ -38908,7 +39503,7 @@ function takeValue(argv, index, name) {
|
|
|
38908
39503
|
}
|
|
38909
39504
|
return value;
|
|
38910
39505
|
}
|
|
38911
|
-
function
|
|
39506
|
+
function parseArgs23(argv) {
|
|
38912
39507
|
const { consumed: focusConsumed } = parseFocusFlags(argv);
|
|
38913
39508
|
const consumed = new Set(focusConsumed);
|
|
38914
39509
|
const profileOverrides = {};
|
|
@@ -39003,7 +39598,7 @@ function parseArgs22(argv) {
|
|
|
39003
39598
|
}
|
|
39004
39599
|
}
|
|
39005
39600
|
const positional = argv.filter((arg, index) => !consumed.has(index) && !arg.startsWith("-"));
|
|
39006
|
-
if (positional.length === 0)
|
|
39601
|
+
if (positional.length === 0) usage25();
|
|
39007
39602
|
const inputPaths = requireInputPaths(positional, "Missing input path.");
|
|
39008
39603
|
requireRenderableInputPaths(inputPaths);
|
|
39009
39604
|
requireSingleInputForOutputPath(inputPaths, outputPath);
|
|
@@ -39087,7 +39682,7 @@ function scriptErrorReport(scriptPath, message, profile, elapsedMs) {
|
|
|
39087
39682
|
};
|
|
39088
39683
|
}
|
|
39089
39684
|
async function runPrintCheckCli(argv = process.argv.slice(2)) {
|
|
39090
|
-
const args =
|
|
39685
|
+
const args = parseArgs23(argv);
|
|
39091
39686
|
requireExistingInputPaths(args.inputPaths);
|
|
39092
39687
|
const profile = resolvePrintProfile(args.profileId, args.profileOverrides);
|
|
39093
39688
|
const reports = [];
|
|
@@ -39322,7 +39917,7 @@ sys.exit(0 if ok else 1)
|
|
|
39322
39917
|
}
|
|
39323
39918
|
|
|
39324
39919
|
// cli/forge-pinocchio.ts
|
|
39325
|
-
function
|
|
39920
|
+
function parseArgs24(argv) {
|
|
39326
39921
|
const inputPaths = [];
|
|
39327
39922
|
let outputPath;
|
|
39328
39923
|
const paramOverrides = {};
|
|
@@ -39369,7 +39964,7 @@ function resolveSimulationModel6(result, jointOverrides) {
|
|
|
39369
39964
|
return collectSimulationModel(def, { state: jointOverrides });
|
|
39370
39965
|
}
|
|
39371
39966
|
async function runPinocchioCli(argv = process.argv.slice(2)) {
|
|
39372
|
-
const { inputPaths, outputPath, paramOverrides, jointOverrides } =
|
|
39967
|
+
const { inputPaths, outputPath, paramOverrides, jointOverrides } = parseArgs24(argv);
|
|
39373
39968
|
requireExistingInputPaths(inputPaths);
|
|
39374
39969
|
let failures = 0;
|
|
39375
39970
|
for (const [index, scriptPath] of inputPaths.entries()) {
|
|
@@ -39407,9 +40002,9 @@ async function exportPinocchioInput(scriptPath, explicitOutputPath, paramOverrid
|
|
|
39407
40002
|
}
|
|
39408
40003
|
|
|
39409
40004
|
// cli/sim-dynamics.ts
|
|
39410
|
-
import { existsSync as
|
|
40005
|
+
import { existsSync as existsSync28, readFileSync as readFileSync33, statSync as statSync12 } from "fs";
|
|
39411
40006
|
import { join as join23, resolve as resolve50 } from "path";
|
|
39412
|
-
function
|
|
40007
|
+
function parseArgs25(argv) {
|
|
39413
40008
|
const inputPaths = [];
|
|
39414
40009
|
let json = false;
|
|
39415
40010
|
let compact = false;
|
|
@@ -39450,12 +40045,12 @@ function failedReport5(source, message, code, suggest) {
|
|
|
39450
40045
|
}
|
|
39451
40046
|
function resolveFeedbackPath2(inputPath) {
|
|
39452
40047
|
const abs = resolve50(inputPath);
|
|
39453
|
-
if (
|
|
40048
|
+
if (existsSync28(abs) && statSync12(abs).isDirectory()) return join23(abs, "feedback.json");
|
|
39454
40049
|
return abs;
|
|
39455
40050
|
}
|
|
39456
40051
|
function ingest(inputPath) {
|
|
39457
40052
|
const feedbackPath = resolveFeedbackPath2(inputPath);
|
|
39458
|
-
if (!
|
|
40053
|
+
if (!existsSync28(feedbackPath)) {
|
|
39459
40054
|
return failedReport5(
|
|
39460
40055
|
inputPath,
|
|
39461
40056
|
`No results at ${feedbackPath}.`,
|
|
@@ -39481,7 +40076,7 @@ function ingest(inputPath) {
|
|
|
39481
40076
|
return { ...report, source: inputPath };
|
|
39482
40077
|
}
|
|
39483
40078
|
async function runSimDynamicsCli(argv = process.argv.slice(2)) {
|
|
39484
|
-
const options =
|
|
40079
|
+
const options = parseArgs25(argv);
|
|
39485
40080
|
const reports = options.inputPaths.map((inputPath) => ingest(inputPath));
|
|
39486
40081
|
printFeedback(reports.length === 1 ? reports[0] : { runs: reports }, options.json, options.compact);
|
|
39487
40082
|
if (reports.some((report) => !report.ok) && !options.noFailExit) process.exitCode = 1;
|
|
@@ -39489,7 +40084,7 @@ async function runSimDynamicsCli(argv = process.argv.slice(2)) {
|
|
|
39489
40084
|
|
|
39490
40085
|
// cli/sim-mass.ts
|
|
39491
40086
|
var DEFAULT_DENSITY_KG_M35 = 1e3;
|
|
39492
|
-
function
|
|
40087
|
+
function parseArgs26(argv) {
|
|
39493
40088
|
const inputPaths = [];
|
|
39494
40089
|
const paramOverrides = {};
|
|
39495
40090
|
const jointOverrides = {};
|
|
@@ -39648,7 +40243,7 @@ function analyze2(source, options, objects) {
|
|
|
39648
40243
|
};
|
|
39649
40244
|
}
|
|
39650
40245
|
async function runSimMassCli(argv = process.argv.slice(2)) {
|
|
39651
|
-
const options =
|
|
40246
|
+
const options = parseArgs26(argv);
|
|
39652
40247
|
requireExistingInputPaths(options.inputPaths);
|
|
39653
40248
|
await init();
|
|
39654
40249
|
const reports = [];
|
|
@@ -39776,7 +40371,7 @@ function analyzeMechanism(input) {
|
|
|
39776
40371
|
|
|
39777
40372
|
// cli/sim-mechanism.ts
|
|
39778
40373
|
var DEFAULT_DENSITY_KG_M36 = 1e3;
|
|
39779
|
-
function
|
|
40374
|
+
function parseArgs27(argv) {
|
|
39780
40375
|
const inputPaths = [];
|
|
39781
40376
|
const paramOverrides = {};
|
|
39782
40377
|
const jointOverrides = {};
|
|
@@ -39871,7 +40466,7 @@ function flattenShapes(part, out) {
|
|
|
39871
40466
|
else if (part instanceof ShapeGroup) part.children.forEach((child) => flattenShapes(child, out));
|
|
39872
40467
|
}
|
|
39873
40468
|
async function runSimMechanismCli(argv = process.argv.slice(2)) {
|
|
39874
|
-
const options =
|
|
40469
|
+
const options = parseArgs27(argv);
|
|
39875
40470
|
requireExistingInputPaths(options.inputPaths);
|
|
39876
40471
|
await init();
|
|
39877
40472
|
const reports = [];
|
|
@@ -40317,7 +40912,7 @@ function resolveToleranceResponses(verifications, specOverrides) {
|
|
|
40317
40912
|
}
|
|
40318
40913
|
|
|
40319
40914
|
// cli/sim-tolerance.ts
|
|
40320
|
-
function
|
|
40915
|
+
function parseArgs28(argv) {
|
|
40321
40916
|
const inputPaths = [];
|
|
40322
40917
|
const paramOverrides = {};
|
|
40323
40918
|
const tolOverrides = /* @__PURE__ */ new Map();
|
|
@@ -40606,7 +41201,7 @@ async function analyze4(scriptPath, options) {
|
|
|
40606
41201
|
};
|
|
40607
41202
|
}
|
|
40608
41203
|
async function runSimToleranceCli(argv = process.argv.slice(2)) {
|
|
40609
|
-
const options =
|
|
41204
|
+
const options = parseArgs28(argv);
|
|
40610
41205
|
requireExistingInputPaths(options.inputPaths);
|
|
40611
41206
|
await init();
|
|
40612
41207
|
const reports = [];
|
|
@@ -41572,9 +42167,9 @@ function parseJointFlags2(argv) {
|
|
|
41572
42167
|
}
|
|
41573
42168
|
return { overrides, consumed };
|
|
41574
42169
|
}
|
|
41575
|
-
function
|
|
42170
|
+
function usage26() {
|
|
41576
42171
|
console.error(
|
|
41577
|
-
"Usage: forgecad run <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [input ...] [--details] [--history] [--features] [--connectivity] [--connectivity-tolerance <mm>] [--param Key=Value] [--joint JointName=Value] [--debug-imports] [--verbose|-v] [--backend manifold|occt|truck|sdf] [--quality live|default|high] [--solver-profile] [--solver-debug-out <dir>]"
|
|
42172
|
+
"Usage: forgecad run <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [input ...] [--details] [--history] [--features] [--connectivity] [--connectivity-tolerance <mm>] [--param Key=Value] [--joint JointName=Value] [--project-root <dir>] [--debug-imports] [--verbose|-v] [--backend manifold|occt|truck|sdf] [--quality live|default|high] [--solver-profile] [--solver-debug-out <dir>]"
|
|
41578
42173
|
);
|
|
41579
42174
|
process.exit(1);
|
|
41580
42175
|
}
|
|
@@ -41773,11 +42368,12 @@ async function runScriptCli(argv = process.argv.slice(2)) {
|
|
|
41773
42368
|
const explicitBackend = parseBackendArg2(argv);
|
|
41774
42369
|
const quality = parseQualityArg2(argv);
|
|
41775
42370
|
const solverDebugOut = parseRequiredArg(argv, "--solver-debug-out");
|
|
42371
|
+
const projectRoot = parseRequiredArg(argv, "--project-root");
|
|
41776
42372
|
const positional = argv.filter(
|
|
41777
|
-
(arg, i) => !paramConsumed.has(i) && !jointConsumed.has(i) && !focusConsumed.has(i) && !connectivityConsumed.has(i) && arg !== "--details" && arg !== "--history" && arg !== "--features" && arg !== "--solver-profile" && arg !== "--debug-imports" && arg !== "--journeys" && arg !== "--journeys-json" && arg !== "--verbose" && arg !== "-v" && arg !== "--backend" && argv[i - 1] !== "--backend" && arg !== "--quality" && argv[i - 1] !== "--quality" && arg !== "-q" && argv[i - 1] !== "-q" && arg !== "--solver-debug-out" && argv[i - 1] !== "--solver-debug-out"
|
|
42373
|
+
(arg, i) => !paramConsumed.has(i) && !jointConsumed.has(i) && !focusConsumed.has(i) && !connectivityConsumed.has(i) && arg !== "--details" && arg !== "--history" && arg !== "--features" && arg !== "--solver-profile" && arg !== "--debug-imports" && arg !== "--journeys" && arg !== "--journeys-json" && arg !== "--verbose" && arg !== "-v" && arg !== "--backend" && argv[i - 1] !== "--backend" && arg !== "--quality" && argv[i - 1] !== "--quality" && arg !== "-q" && argv[i - 1] !== "-q" && arg !== "--solver-debug-out" && argv[i - 1] !== "--solver-debug-out" && arg !== "--project-root" && argv[i - 1] !== "--project-root"
|
|
41778
42374
|
);
|
|
41779
42375
|
const scriptPaths = positional;
|
|
41780
|
-
if (scriptPaths.length === 0)
|
|
42376
|
+
if (scriptPaths.length === 0) usage26();
|
|
41781
42377
|
requireRenderableInputPaths(scriptPaths);
|
|
41782
42378
|
requireExistingInputPaths(scriptPaths);
|
|
41783
42379
|
if (scriptPaths.length > 1 && printJourneysJson) {
|
|
@@ -41799,7 +42395,7 @@ async function runScriptCli(argv = process.argv.slice(2)) {
|
|
|
41799
42395
|
debugSvgSnapshots: true
|
|
41800
42396
|
});
|
|
41801
42397
|
}
|
|
41802
|
-
const input = loadCliScriptInput(scriptPath);
|
|
42398
|
+
const input = loadCliScriptInput(scriptPath, { projectRoot });
|
|
41803
42399
|
activeBackend = resolveCliBackend(explicitBackend, input) ?? CLI_DEFAULT_BACKEND;
|
|
41804
42400
|
await initCliBackend(activeBackend);
|
|
41805
42401
|
resetSolverStats();
|
|
@@ -41988,13 +42584,36 @@ async function runScriptCli(argv = process.argv.slice(2)) {
|
|
|
41988
42584
|
const connectivity = analyzePhysicalConnectivity2(entries, connectivityOptions);
|
|
41989
42585
|
for (const line of formatPhysicalConnectivity(connectivity)) console.log(line);
|
|
41990
42586
|
}
|
|
41991
|
-
|
|
41992
|
-
|
|
41993
|
-
|
|
41994
|
-
|
|
41995
|
-
|
|
41996
|
-
|
|
41997
|
-
|
|
42587
|
+
if (result.params.length > 0) {
|
|
42588
|
+
console.log(
|
|
42589
|
+
`\u2713 Params: ${result.params.map((p) => {
|
|
42590
|
+
const label = p.choices ? `${p.choices[p.value]}` : `${p.value}`;
|
|
42591
|
+
const changed = p.value !== p.defaultValue ? " *" : "";
|
|
42592
|
+
return `${p.name}=${label}${changed}`;
|
|
42593
|
+
}).join(", ")}`
|
|
42594
|
+
);
|
|
42595
|
+
}
|
|
42596
|
+
if (result.stringParams.length > 0) {
|
|
42597
|
+
console.log(`\u2713 String Params: ${result.stringParams.map((p) => `${p.name}=${JSON.stringify(p.value)}`).join(", ")}`);
|
|
42598
|
+
}
|
|
42599
|
+
if (result.listParams.length > 0) {
|
|
42600
|
+
console.log(`\u2713 List Params: ${result.listParams.map((p) => `${p.name} (${p.items.length} rows)`).join(", ")}`);
|
|
42601
|
+
}
|
|
42602
|
+
if (result.path2dParams.length > 0) {
|
|
42603
|
+
console.log(
|
|
42604
|
+
`\u2713 Path Params: ${result.path2dParams.map((p) => `${p.name} (${p.points.length} ${p.closed ? "outline" : "centerline"} points)`).join(", ")}`
|
|
42605
|
+
);
|
|
42606
|
+
}
|
|
42607
|
+
if (result.spline2dParams.length > 0) {
|
|
42608
|
+
console.log(
|
|
42609
|
+
`\u2713 Spline Params: ${result.spline2dParams.map((p) => `${p.name} (${p.points.length} points, degree ${p.degree})`).join(", ")}`
|
|
42610
|
+
);
|
|
42611
|
+
}
|
|
42612
|
+
if (result.placement2dParams.length > 0) {
|
|
42613
|
+
console.log(
|
|
42614
|
+
`\u2713 Placement Params: ${result.placement2dParams.map((p) => `${p.name} (${p.items.length} items${p.zones.length > 0 ? `, ${p.zones.length} zones` : ""})`).join(", ")}`
|
|
42615
|
+
);
|
|
42616
|
+
}
|
|
41998
42617
|
console.log(`\u2713 Time: ${result.timeMs.toFixed(0)}ms`);
|
|
41999
42618
|
if (!printSolverProfile && solverDebugOut) {
|
|
42000
42619
|
const bundles = await writeSolverDebugArtifacts(solverDebugOut, scriptPath, result.objects);
|
|
@@ -42155,7 +42774,7 @@ async function runScriptCli(argv = process.argv.slice(2)) {
|
|
|
42155
42774
|
// cli/forge-render-section.ts
|
|
42156
42775
|
import { writeFile as writeFile11 } from "fs/promises";
|
|
42157
42776
|
import { basename as basename20, extname as extname16, resolve as resolve53 } from "path";
|
|
42158
|
-
function
|
|
42777
|
+
function usage27() {
|
|
42159
42778
|
console.error(
|
|
42160
42779
|
"Usage: forgecad render section <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [input ...] [--output path] [--format svg|png] [--param Key=Value] [--joint JointName=Value] [--plane XY|XZ|YZ] [--offset <number>] [--size <px>] [--edges <off|thin|bold>] [--chrome-path <path>] [--background <color>]"
|
|
42161
42780
|
);
|
|
@@ -42233,7 +42852,7 @@ function parseRenderSectionArgs(argv) {
|
|
|
42233
42852
|
background = argv[++i];
|
|
42234
42853
|
} else if (argv[i].startsWith("-")) {
|
|
42235
42854
|
console.error(`Unknown option: ${argv[i]}`);
|
|
42236
|
-
|
|
42855
|
+
usage27();
|
|
42237
42856
|
} else {
|
|
42238
42857
|
inputPaths.push(argv[i]);
|
|
42239
42858
|
}
|
|
@@ -42324,7 +42943,7 @@ async function renderSectionInput(options, scriptPath, resolvedChromePath) {
|
|
|
42324
42943
|
|
|
42325
42944
|
// cli/update-check.ts
|
|
42326
42945
|
import { spawn as spawn4 } from "child_process";
|
|
42327
|
-
import { existsSync as
|
|
42946
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync17, readFileSync as readFileSync34, writeFileSync as writeFileSync25 } from "fs";
|
|
42328
42947
|
import { homedir as homedir9 } from "os";
|
|
42329
42948
|
import { dirname as dirname15, join as join25 } from "path";
|
|
42330
42949
|
var PACKAGE_NAME = "forgecad";
|
|
@@ -42348,7 +42967,7 @@ function readCache() {
|
|
|
42348
42967
|
function writeCache(cache) {
|
|
42349
42968
|
const p = cachePath();
|
|
42350
42969
|
const dir = dirname15(p);
|
|
42351
|
-
if (!
|
|
42970
|
+
if (!existsSync29(dir)) mkdirSync17(dir, { recursive: true });
|
|
42352
42971
|
writeFileSync25(p, JSON.stringify(cache), "utf-8");
|
|
42353
42972
|
}
|
|
42354
42973
|
function shouldSkip() {
|
|
@@ -43948,9 +44567,9 @@ var commands = [
|
|
|
43948
44567
|
group: "Modeling",
|
|
43949
44568
|
path: ["run"],
|
|
43950
44569
|
summary: "Execute a Forge script quickly and print the inner-loop build summary: build count, verification results, parameters, and timing.",
|
|
43951
|
-
description: "The fast validation command: runs the script with the real geometry kernel (no browser) and reports build status, object count, `verify.*` pass/fail with expected vs actual values (non-fatal \u2014 the model still renders), parameter values, script logs, and timing. Run it frequently while editing.\n\nA bare `forgecad run` skips expensive diagnostics. Opt in with `--details` (volumes/bounds), `--history` (construction tree), `--features` (feature tallies), `--solver-profile` (constraint solver timing), or `--connectivity` (physical connected components \u2014 bbox contact is evidence, exact geometry is checked by default). `--quality live|default|high` selects the same geometry quality profile as the editor and export tools; `live` is fastest for large models.\n\nDirect `.stl`/`.obj`/`.3mf`/`.step`/`.stp` inputs are imported automatically; STEP/STP auto-selects OCCT unless you pass `--backend`. For deeper confidence gates, prefer `inspect mechanical-integrity`, `check print`, or `inspect fit interference` instead of turning `run` into a catch-all audit command.",
|
|
44570
|
+
description: "The fast validation command: runs the script with the real geometry kernel (no browser) and reports build status, object count, `verify.*` pass/fail with expected vs actual values (non-fatal \u2014 the model still renders), parameter values, script logs, and timing. Run it frequently while editing.\n\nA bare `forgecad run` skips expensive diagnostics. Opt in with `--details` (volumes/bounds), `--history` (construction tree), `--features` (feature tallies), `--solver-profile` (constraint solver timing), or `--connectivity` (physical connected components \u2014 bbox contact is evidence, exact geometry is checked by default). `--quality live|default|high` selects the same geometry quality profile as the editor and export tools; `live` is fastest for large models.\n\nDirect `.stl`/`.obj`/`.3mf`/`.step`/`.stp` inputs are imported automatically; STEP/STP auto-selects OCCT unless you pass `--backend`. Use `--project-root <dir>` when running a file inside a larger uninitialized folder and you intentionally want relative imports to resolve against that root. For deeper confidence gates, prefer `inspect mechanical-integrity`, `check print`, or `inspect fit interference` instead of turning `run` into a catch-all audit command.",
|
|
43952
44571
|
usage: [
|
|
43953
|
-
"forgecad run <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [input ...] [--details] [--history] [--features] [--connectivity] [--connectivity-tolerance <mm>] [--focus [names]] [--hide names] [--journeys] [--journeys-json] [--param Key=Value] [--joint JointName=Value] [--debug-imports] [--verbose] [--backend manifold|occt|truck|sdf] [--quality live|default|high] [--solver-profile] [--solver-debug-out <dir>]"
|
|
44572
|
+
"forgecad run <model.forge.js|asset.stl|asset.obj|asset.3mf|asset.step|asset.stp> [input ...] [--details] [--history] [--features] [--connectivity] [--connectivity-tolerance <mm>] [--focus [names]] [--hide names] [--journeys] [--journeys-json] [--param Key=Value] [--joint JointName=Value] [--project-root <dir>] [--debug-imports] [--verbose] [--backend manifold|occt|truck|sdf] [--quality live|default|high] [--solver-profile] [--solver-debug-out <dir>]"
|
|
43954
44573
|
],
|
|
43955
44574
|
examples: [
|
|
43956
44575
|
"forgecad run examples/api/static-assembly-connectors.forge.js",
|
|
@@ -43964,6 +44583,7 @@ var commands = [
|
|
|
43964
44583
|
"forgecad run examples/products/cup.forge.js --backend occt",
|
|
43965
44584
|
"forgecad run examples/products/cup.forge.js --backend truck --quality live",
|
|
43966
44585
|
"forgecad run examples/products/cup.forge.js --debug-imports",
|
|
44586
|
+
"forgecad run assembly/main.forge.js --project-root .",
|
|
43967
44587
|
'forgecad run examples/products/cup.forge.js -p "Wall Thickness=3" -p "Body Height=200"',
|
|
43968
44588
|
"forgecad run examples/constraints/06-complex-spectrogram.forge.js --solver-debug-out tmp/spectrogram-debug"
|
|
43969
44589
|
],
|
|
@@ -43990,6 +44610,13 @@ var commands = [
|
|
|
43990
44610
|
valueLabel: "<mm>"
|
|
43991
44611
|
},
|
|
43992
44612
|
{ name: "--debug-imports", description: "Print the import trace" },
|
|
44613
|
+
{
|
|
44614
|
+
name: "--project-root",
|
|
44615
|
+
description: "Project/import root for this run; enables intentional relative imports within that directory",
|
|
44616
|
+
argument: "required",
|
|
44617
|
+
valueLabel: "<dir>",
|
|
44618
|
+
valueKind: "directory"
|
|
44619
|
+
},
|
|
43993
44620
|
{ name: "--journeys", description: "Print model journey summaries declared with scene({ journeys })" },
|
|
43994
44621
|
{ name: "--journeys-json", description: "Emit machine-readable journey metadata and diagnostics only" },
|
|
43995
44622
|
{
|
|
@@ -44509,11 +45136,22 @@ var commands = [
|
|
|
44509
45136
|
options: [
|
|
44510
45137
|
...PARAM_OPTIONS,
|
|
44511
45138
|
{ name: "--study", description: "Run one named FEA study", argument: "required", valueLabel: "<name>" },
|
|
44512
|
-
{
|
|
45139
|
+
{
|
|
45140
|
+
name: "--output",
|
|
45141
|
+
description: "Output result root directory",
|
|
45142
|
+
argument: "required",
|
|
45143
|
+
valueLabel: "<dir>",
|
|
45144
|
+
valueKind: "directory"
|
|
45145
|
+
},
|
|
44513
45146
|
{ name: "--no-render", description: "Run solver and write reports without PNG render artifacts" },
|
|
44514
45147
|
{ name: "--camera", description: "Camera/view label for stress renders", argument: "required", valueLabel: "<view>" },
|
|
44515
45148
|
{ name: "--size", description: "Render image size in pixels", argument: "required", valueLabel: "<px>" },
|
|
44516
|
-
{
|
|
45149
|
+
{
|
|
45150
|
+
name: "--chrome-path",
|
|
45151
|
+
description: "Chrome/Chromium executable path for PNG rendering",
|
|
45152
|
+
argument: "required",
|
|
45153
|
+
valueLabel: "<path>"
|
|
45154
|
+
},
|
|
44517
45155
|
{ name: "--json", description: "Emit the FEA result index JSON" },
|
|
44518
45156
|
{ name: "--compact", description: "Minify JSON output" },
|
|
44519
45157
|
{ name: "--no-fail-exit", description: "Always exit 0 after writing the result bundle" }
|
|
@@ -44566,11 +45204,21 @@ var commands = [
|
|
|
44566
45204
|
{ value: "deformed", description: "CalculiX displacement shape" }
|
|
44567
45205
|
]
|
|
44568
45206
|
},
|
|
44569
|
-
{
|
|
45207
|
+
{
|
|
45208
|
+
name: "--exaggerate",
|
|
45209
|
+
description: "Deformation exaggeration factor; requires --shape deformed",
|
|
45210
|
+
argument: "required",
|
|
45211
|
+
valueLabel: "<x>"
|
|
45212
|
+
},
|
|
44570
45213
|
{ name: "--output", description: "Output PNG path", argument: "required", valueLabel: "<path>", valueKind: "path" },
|
|
44571
45214
|
{ name: "--camera", description: "Camera/view label for stress renders", argument: "required", valueLabel: "<view>" },
|
|
44572
45215
|
{ name: "--size", description: "Render image size in pixels", argument: "required", valueLabel: "<px>" },
|
|
44573
|
-
{
|
|
45216
|
+
{
|
|
45217
|
+
name: "--chrome-path",
|
|
45218
|
+
description: "Chrome/Chromium executable path for PNG rendering",
|
|
45219
|
+
argument: "required",
|
|
45220
|
+
valueLabel: "<path>"
|
|
45221
|
+
}
|
|
44574
45222
|
],
|
|
44575
45223
|
positionals: [{ description: "FEA study result directory", valueKind: "directory" }]
|
|
44576
45224
|
},
|
|
@@ -44585,10 +45233,20 @@ var commands = [
|
|
|
44585
45233
|
completion: {
|
|
44586
45234
|
options: [
|
|
44587
45235
|
{ name: "--output", description: "Output comparison directory", argument: "required", valueLabel: "<dir>", valueKind: "directory" },
|
|
44588
|
-
{
|
|
45236
|
+
{
|
|
45237
|
+
name: "--field",
|
|
45238
|
+
description: "Comparison field. Only safety is supported so scales stay locked.",
|
|
45239
|
+
argument: "required",
|
|
45240
|
+
valueLabel: "<safety>"
|
|
45241
|
+
},
|
|
44589
45242
|
{ name: "--camera", description: "Shared render camera", argument: "required", valueLabel: "<name>" },
|
|
44590
45243
|
{ name: "--size", description: "Shared render image size in pixels", argument: "required", valueLabel: "<px>" },
|
|
44591
|
-
{
|
|
45244
|
+
{
|
|
45245
|
+
name: "--chrome-path",
|
|
45246
|
+
description: "Chrome/Chromium executable path for PNG rendering",
|
|
45247
|
+
argument: "required",
|
|
45248
|
+
valueLabel: "<path>"
|
|
45249
|
+
}
|
|
44592
45250
|
],
|
|
44593
45251
|
positionals: [
|
|
44594
45252
|
{ description: "First FEA study result directory", valueKind: "directory" },
|
|
@@ -44607,7 +45265,13 @@ var commands = [
|
|
|
44607
45265
|
options: [
|
|
44608
45266
|
...PARAM_OPTIONS,
|
|
44609
45267
|
{ name: "--study", description: "Run one named FEA study", argument: "required", valueLabel: "<name>" },
|
|
44610
|
-
{
|
|
45268
|
+
{
|
|
45269
|
+
name: "--output",
|
|
45270
|
+
description: "Output result root directory",
|
|
45271
|
+
argument: "required",
|
|
45272
|
+
valueLabel: "<dir>",
|
|
45273
|
+
valueKind: "directory"
|
|
45274
|
+
},
|
|
44611
45275
|
{ name: "--json", description: "Emit the FEA result index JSON" },
|
|
44612
45276
|
{ name: "--compact", description: "Minify JSON output" },
|
|
44613
45277
|
{ name: "--no-fail-exit", description: "Always exit 0 after writing the result bundle" }
|
|
@@ -45157,10 +45821,50 @@ var commands = [
|
|
|
45157
45821
|
},
|
|
45158
45822
|
run: runContextRenderCli
|
|
45159
45823
|
},
|
|
45824
|
+
{
|
|
45825
|
+
group: "Project",
|
|
45826
|
+
path: ["manual-edits"],
|
|
45827
|
+
summary: "Read submitted manual parameter edits for a ForgeCAD entry file.",
|
|
45828
|
+
description: "Agent-facing manual edit commands. The browser submits manual path and spline canvas edits when the user chooses Send to AI; these commands let a local agent read the exact point and G-continuity data before updating source defaults.",
|
|
45829
|
+
usage: ["forgecad manual-edits <command>"],
|
|
45830
|
+
examples: ["forgecad manual-edits status main.forge.js", "forgecad manual-edits get main.forge.js --format json"],
|
|
45831
|
+
run: async () => {
|
|
45832
|
+
console.error("`forgecad manual-edits` requires a subcommand.");
|
|
45833
|
+
console.error("");
|
|
45834
|
+
console.error("Available subcommands:");
|
|
45835
|
+
console.error(" forgecad manual-edits get <model.forge.js> [--format text|json]");
|
|
45836
|
+
console.error(" forgecad manual-edits status <model.forge.js> [--format text|json]");
|
|
45837
|
+
process.exit(1);
|
|
45838
|
+
}
|
|
45839
|
+
},
|
|
45840
|
+
{
|
|
45841
|
+
group: "Project",
|
|
45842
|
+
path: ["manual-edits", "get"],
|
|
45843
|
+
summary: "Print the latest submitted manual parameter edits for one entry file.",
|
|
45844
|
+
usage: ["forgecad manual-edits get <model.forge.js> [--format text|json]"],
|
|
45845
|
+
examples: ["forgecad manual-edits get main.forge.js", "forgecad manual-edits get main.forge.js --format json"],
|
|
45846
|
+
completion: {
|
|
45847
|
+
positionals: [{ description: "ForgeCAD entry file", valueKind: "file" }],
|
|
45848
|
+
options: [{ name: "--format", description: "Output format", argument: "required", valueLabel: "<text|json>" }]
|
|
45849
|
+
},
|
|
45850
|
+
run: runManualEditsGetCli
|
|
45851
|
+
},
|
|
45852
|
+
{
|
|
45853
|
+
group: "Project",
|
|
45854
|
+
path: ["manual-edits", "status"],
|
|
45855
|
+
summary: "Show whether one entry file has submitted manual parameter edits.",
|
|
45856
|
+
usage: ["forgecad manual-edits status <model.forge.js> [--format text|json]"],
|
|
45857
|
+
examples: ["forgecad manual-edits status main.forge.js", "forgecad manual-edits status main.forge.js --format json"],
|
|
45858
|
+
completion: {
|
|
45859
|
+
positionals: [{ description: "ForgeCAD entry file", valueKind: "file" }],
|
|
45860
|
+
options: [{ name: "--format", description: "Output format", argument: "required", valueLabel: "<text|json>" }]
|
|
45861
|
+
},
|
|
45862
|
+
run: runManualEditsStatusCli
|
|
45863
|
+
},
|
|
45160
45864
|
{
|
|
45161
45865
|
group: "Project",
|
|
45162
45866
|
path: ["project", "init"],
|
|
45163
|
-
summary: "Initialize the current directory as a ForgeCAD project
|
|
45867
|
+
summary: "Initialize the current directory as a local ForgeCAD project.",
|
|
45164
45868
|
usage: ["forgecad project init [name] [--slug <slug>] [--visibility <private|shared|public>]"],
|
|
45165
45869
|
examples: ["forgecad project init", 'forgecad project init "My Gearbox" --slug my-gearbox'],
|
|
45166
45870
|
completion: {
|
|
@@ -45210,7 +45914,7 @@ var commands = [
|
|
|
45210
45914
|
{
|
|
45211
45915
|
group: "Project",
|
|
45212
45916
|
path: ["project", "push"],
|
|
45213
|
-
summary: "
|
|
45917
|
+
summary: "Create the hosted project if needed, then upload local changes.",
|
|
45214
45918
|
usage: ["forgecad project push [--force] [--delete-missing]"],
|
|
45215
45919
|
examples: ["forgecad project push", "forgecad project push --delete-missing"],
|
|
45216
45920
|
completion: {
|
|
@@ -46384,7 +47088,7 @@ var commands = [
|
|
|
46384
47088
|
{ name: "--update", description: "Regenerate compiler snapshots" }
|
|
46385
47089
|
]
|
|
46386
47090
|
},
|
|
46387
|
-
run: async (args) => (await import("./check-compiler-
|
|
47091
|
+
run: async (args) => (await import("./check-compiler-UJWUEIDC.js")).runCheckCompilerCli(args)
|
|
46388
47092
|
},
|
|
46389
47093
|
{
|
|
46390
47094
|
group: "Checks",
|
|
@@ -46407,7 +47111,7 @@ var commands = [
|
|
|
46407
47111
|
{ name: "--update", description: "Regenerate query-propagation snapshots" }
|
|
46408
47112
|
]
|
|
46409
47113
|
},
|
|
46410
|
-
run: async (args) => (await import("./check-query-propagation-
|
|
47114
|
+
run: async (args) => (await import("./check-query-propagation-O2EPDJSY.js")).runCheckQueryPropagationCli(args)
|
|
46411
47115
|
},
|
|
46412
47116
|
{
|
|
46413
47117
|
group: "Checks",
|
|
@@ -46825,8 +47529,8 @@ function isOptionToken(value) {
|
|
|
46825
47529
|
return /^--?[A-Za-z]/.test(value);
|
|
46826
47530
|
}
|
|
46827
47531
|
function supportsEqualsOptionSyntax(command, optionName) {
|
|
46828
|
-
const
|
|
46829
|
-
return (
|
|
47532
|
+
const path7 = command.path.join(" ");
|
|
47533
|
+
return (path7 === "signup" || path7 === "login") && optionName === "--server" || path7 === "login" && optionName === "--token" || path7 === "skill install" && optionName === "--target";
|
|
46830
47534
|
}
|
|
46831
47535
|
function validateCommandArgs(command, args) {
|
|
46832
47536
|
if (command.hidden) return;
|
|
@@ -46993,17 +47697,17 @@ function oclifSummaryFor(command) {
|
|
|
46993
47697
|
function stripAnsiCodes(value) {
|
|
46994
47698
|
return value.replace(/\x1b\[[0-9;]*m/g, "");
|
|
46995
47699
|
}
|
|
46996
|
-
function commandPathId(
|
|
46997
|
-
return
|
|
47700
|
+
function commandPathId(path7) {
|
|
47701
|
+
return path7.join(" ");
|
|
46998
47702
|
}
|
|
46999
|
-
function findCommandDefinition(
|
|
47000
|
-
const id = commandPathId(
|
|
47703
|
+
function findCommandDefinition(path7) {
|
|
47704
|
+
const id = commandPathId(path7);
|
|
47001
47705
|
return commands.find((command) => !command.hidden && commandPathId(command.path) === id);
|
|
47002
47706
|
}
|
|
47003
|
-
function commandOrTopicSummary(
|
|
47004
|
-
const command = findCommandDefinition(
|
|
47707
|
+
function commandOrTopicSummary(path7) {
|
|
47708
|
+
const command = findCommandDefinition(path7);
|
|
47005
47709
|
if (command) return oclifSummaryFor(command);
|
|
47006
|
-
return TOPIC_DESCRIPTIONS[commandPathId(
|
|
47710
|
+
return TOPIC_DESCRIPTIONS[commandPathId(path7)];
|
|
47007
47711
|
}
|
|
47008
47712
|
function visibleChildCommands(topicPath) {
|
|
47009
47713
|
return commands.filter((command) => {
|
|
@@ -47051,7 +47755,7 @@ function topicNameForHelp(topic) {
|
|
|
47051
47755
|
var ForgeCadHelp = class extends Help {
|
|
47052
47756
|
async showRootHelp() {
|
|
47053
47757
|
const groupSections = ROOT_HELP_GROUPS.map(({ heading, paths }) => {
|
|
47054
|
-
const rows = paths.map((
|
|
47758
|
+
const rows = paths.map((path7) => [`forgecad ${commandPathId(path7)}`, commandOrTopicSummary(path7)]);
|
|
47055
47759
|
return `${heading}
|
|
47056
47760
|
${helpRows(rows)}`;
|
|
47057
47761
|
});
|