@forgeax/engine-devkit 0.1.3 → 0.1.6
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/README.md +30 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/cli-output.unit.test.d.ts +2 -0
- package/dist/__tests__/cli-output.unit.test.d.ts.map +1 -0
- package/dist/__tests__/gltf-source-key-diagnostics.integration.test.d.ts +2 -0
- package/dist/__tests__/gltf-source-key-diagnostics.integration.test.d.ts.map +1 -0
- package/dist/__tests__/preview-port.integration.test.d.ts +2 -0
- package/dist/__tests__/preview-port.integration.test.d.ts.map +1 -0
- package/dist/__tests__/rhi-debug-build-graph.integration.test.d.ts +2 -0
- package/dist/__tests__/rhi-debug-build-graph.integration.test.d.ts.map +1 -0
- package/dist/__tests__/sdk-update.unit.test.d.ts +2 -0
- package/dist/__tests__/sdk-update.unit.test.d.ts.map +1 -0
- package/dist/assets.d.ts.map +1 -1
- package/dist/bootstrap-commands.d.ts.map +1 -1
- package/dist/cli-output.d.ts +4 -0
- package/dist/cli-output.d.ts.map +1 -0
- package/dist/cli.mjs +728 -641
- package/dist/cli.mjs.map +1 -1
- package/dist/commands.d.ts.map +1 -1
- package/dist/host/__tests__/bootstrap-root-owner.test-d.d.ts +2 -0
- package/dist/host/__tests__/bootstrap-root-owner.test-d.d.ts.map +1 -0
- package/dist/host/project-bootstrap.d.ts +4 -2
- package/dist/host/project-bootstrap.d.ts.map +1 -1
- package/dist/host/resource-bootstrap.d.ts +5 -3
- package/dist/host/resource-bootstrap.d.ts.map +1 -1
- package/dist/host.d.ts +2 -1
- package/dist/host.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +662 -655
- package/dist/index.mjs.map +1 -1
- package/dist/init.d.ts +2 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/project.d.ts +0 -1
- package/dist/project.d.ts.map +1 -1
- package/dist/rhi-debug/cli-context.d.ts.map +1 -1
- package/dist/sdk-bootstrap.d.ts +34 -0
- package/dist/sdk-bootstrap.d.ts.map +1 -0
- package/dist/sdk-cli.mjs +412 -66
- package/dist/sdk-cli.mjs.map +1 -1
- package/dist/sdk-install.d.ts.map +1 -1
- package/dist/sdk-update.d.ts +23 -0
- package/dist/sdk-update.d.ts.map +1 -0
- package/dist/tools/benchmark/__tests__/benchmark-vocabulary-owner.test-d.d.ts +2 -0
- package/dist/tools/benchmark/__tests__/benchmark-vocabulary-owner.test-d.d.ts.map +1 -0
- package/dist/tools/benchmark/harness.d.ts.map +1 -1
- package/dist/tools/browser-host.d.ts.map +1 -1
- package/dist/tools/native-preview.d.ts.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +29 -30
- package/dist/__tests__/tool-migration.test.d.ts +0 -2
- package/dist/__tests__/tool-migration.test.d.ts.map +0 -1
package/dist/sdk-cli.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { execFile, spawn } from 'child_process';
|
|
2
3
|
import { resolve, dirname, basename, relative, sep, isAbsolute } from 'path';
|
|
3
|
-
import { pathToFileURL, fileURLToPath } from 'url';
|
|
4
|
-
import { execFile } from 'child_process';
|
|
5
4
|
import { readdir, mkdir, mkdtemp, cp, rename, rm, copyFile, readFile, realpath, writeFile, symlink, access, lstat, readlink, rmdir } from 'fs/promises';
|
|
6
5
|
import { promisify } from 'util';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import { tmpdir } from 'os';
|
|
7
8
|
|
|
8
9
|
var __defProp = Object.defineProperty;
|
|
9
10
|
var __export = (target, all) => {
|
|
@@ -126,6 +127,7 @@ function createInitPlan(facts, sdk) {
|
|
|
126
127
|
value: {
|
|
127
128
|
root: facts.root,
|
|
128
129
|
version: sdk?.sdkVersion ?? devkitVersion,
|
|
130
|
+
...sdk === void 0 ? {} : { pnpmVersion: sdk.requirements.pnpm },
|
|
129
131
|
archiveBacked: sdk !== void 0,
|
|
130
132
|
dependencyChanges,
|
|
131
133
|
scriptChanges
|
|
@@ -146,23 +148,8 @@ async function applyInitPlan(facts, plan, options) {
|
|
|
146
148
|
for (const change of plan.scriptChanges) scripts[change.name] = change.to;
|
|
147
149
|
manifest.scripts = scripts;
|
|
148
150
|
if (plan.archiveBacked) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const pnpm = pnpmValue !== null && typeof pnpmValue === "object" ? pnpmValue : {};
|
|
152
|
-
manifest.pnpm = {
|
|
153
|
-
...pnpm,
|
|
154
|
-
onlyBuiltDependencies: [
|
|
155
|
-
"@forgeax/engine-codec",
|
|
156
|
-
"@forgeax/engine-fbx",
|
|
157
|
-
"@forgeax/engine-wgpu-wasm",
|
|
158
|
-
"esbuild"
|
|
159
|
-
],
|
|
160
|
-
supportedArchitectures: {
|
|
161
|
-
os: ["darwin", "linux", "win32"],
|
|
162
|
-
cpu: ["x64", "arm64"],
|
|
163
|
-
libc: ["glibc", "musl"]
|
|
164
|
-
}
|
|
165
|
-
};
|
|
151
|
+
if (plan.pnpmVersion === void 0) throw new Error("sdk-pnpm-version-missing");
|
|
152
|
+
manifest.packageManager = `pnpm@${plan.pnpmVersion}`;
|
|
166
153
|
}
|
|
167
154
|
await writeFile(resolve(facts.root, "package.json"), `${JSON.stringify(manifest, null, 2)}
|
|
168
155
|
`);
|
|
@@ -4684,7 +4671,7 @@ var coerce = {
|
|
|
4684
4671
|
};
|
|
4685
4672
|
var NEVER = INVALID;
|
|
4686
4673
|
|
|
4687
|
-
// ../
|
|
4674
|
+
// ../project/dist/index.mjs
|
|
4688
4675
|
var GuidString = external_exports.string().refine(
|
|
4689
4676
|
(val) => AssetGuid.parse(val).ok,
|
|
4690
4677
|
(val) => {
|
|
@@ -4789,7 +4776,7 @@ async function readJson(path) {
|
|
|
4789
4776
|
function firstUnsupportedStandaloneRealm(entries, inheritedRealm = "engine") {
|
|
4790
4777
|
for (const entry of entries) {
|
|
4791
4778
|
const realm = entry.realm ?? inheritedRealm ?? "engine";
|
|
4792
|
-
if (realm
|
|
4779
|
+
if (realm !== "engine") return { id: entry.id, realm };
|
|
4793
4780
|
if (entry.group === true) {
|
|
4794
4781
|
const unsupported = firstUnsupportedStandaloneRealm(
|
|
4795
4782
|
entry.config,
|
|
@@ -4907,13 +4894,10 @@ async function readProjectFacts(rootInput = process.cwd()) {
|
|
|
4907
4894
|
}
|
|
4908
4895
|
};
|
|
4909
4896
|
}
|
|
4910
|
-
function isCommandError(value) {
|
|
4911
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
4912
|
-
const candidate = value;
|
|
4913
|
-
return typeof candidate.code === "string" && typeof candidate.expected === "string" && typeof candidate.hint === "string" && candidate.detail !== null && typeof candidate.detail === "object" && !Array.isArray(candidate.detail);
|
|
4914
|
-
}
|
|
4915
4897
|
function commandError(cause, fallbackCode) {
|
|
4916
|
-
if (
|
|
4898
|
+
if (cause !== null && typeof cause === "object" && "code" in cause && "expected" in cause && "hint" in cause && "detail" in cause) {
|
|
4899
|
+
return cause;
|
|
4900
|
+
}
|
|
4917
4901
|
return {
|
|
4918
4902
|
code: fallbackCode,
|
|
4919
4903
|
expected: "the ForgeaX command to complete",
|
|
@@ -4955,6 +4939,260 @@ async function findSdkContext() {
|
|
|
4955
4939
|
cursor = parent;
|
|
4956
4940
|
}
|
|
4957
4941
|
}
|
|
4942
|
+
var execFileAsync = promisify(execFile);
|
|
4943
|
+
var SDK_INIT_PATH = [".forgeax", "sdk-init.json"];
|
|
4944
|
+
var SDK_INIT_SCHEMA_VERSION = "1.0.0";
|
|
4945
|
+
function agentOnboarding(sdk, projectRoot) {
|
|
4946
|
+
const root2 = projectRoot ?? sdk.root;
|
|
4947
|
+
return {
|
|
4948
|
+
read: [
|
|
4949
|
+
resolve(root2, "AGENTS.md"),
|
|
4950
|
+
resolve(root2, "skills", "forgeax-engine-sdk", "SKILL.md"),
|
|
4951
|
+
resolve(root2, "skills", "forgeax-engine-sdk", "references", "feature-catalog.md")
|
|
4952
|
+
],
|
|
4953
|
+
next: projectRoot === void 0 ? {
|
|
4954
|
+
cwd: root2,
|
|
4955
|
+
argv: ["node", "./bin/forgeax.mjs", "new", "../my-game"]
|
|
4956
|
+
} : { cwd: root2, argv: ["pnpm", "exec", "forgeax", "list", "--json"] }
|
|
4957
|
+
};
|
|
4958
|
+
}
|
|
4959
|
+
function sdkInitPath(sdk) {
|
|
4960
|
+
return resolve(sdk.root, ...SDK_INIT_PATH);
|
|
4961
|
+
}
|
|
4962
|
+
function commandFailure(code, expected, hint, detail = {}) {
|
|
4963
|
+
return { ok: false, error: { code, expected, hint, detail } };
|
|
4964
|
+
}
|
|
4965
|
+
function sdkProjectInstallArgs(store) {
|
|
4966
|
+
const common = [
|
|
4967
|
+
"install",
|
|
4968
|
+
"--frozen-lockfile",
|
|
4969
|
+
"--ignore-scripts",
|
|
4970
|
+
"--side-effects-cache=true",
|
|
4971
|
+
"--child-concurrency=1"
|
|
4972
|
+
];
|
|
4973
|
+
return store === void 0 ? common : [...common, "--offline", "--config.trust-lockfile=true", "--store-dir", store];
|
|
4974
|
+
}
|
|
4975
|
+
function sdkBootstrapInstallArgs(store) {
|
|
4976
|
+
const common = [
|
|
4977
|
+
"install",
|
|
4978
|
+
"--frozen-lockfile",
|
|
4979
|
+
"--child-concurrency=1",
|
|
4980
|
+
"--side-effects-cache=true"
|
|
4981
|
+
];
|
|
4982
|
+
return store === void 0 ? common : [...common, "--offline", "--config.trust-lockfile=true", "--store-dir", store];
|
|
4983
|
+
}
|
|
4984
|
+
function supportedPnpm(version) {
|
|
4985
|
+
const [major = 0, minor = 0] = version.split(".").map(Number);
|
|
4986
|
+
return major === 11 && minor >= 7;
|
|
4987
|
+
}
|
|
4988
|
+
async function currentPnpm() {
|
|
4989
|
+
try {
|
|
4990
|
+
const result = await execFileAsync("pnpm", ["--version"], { maxBuffer: 1024 * 1024 });
|
|
4991
|
+
const version = result.stdout.trim();
|
|
4992
|
+
if (version.length === 0) throw new Error("pnpm returned an empty version");
|
|
4993
|
+
return { ok: true, value: version };
|
|
4994
|
+
} catch (cause) {
|
|
4995
|
+
return commandFailure(
|
|
4996
|
+
"pnpm-unavailable",
|
|
4997
|
+
"pnpm 11.7.0 or newer in the pnpm 11 line to be available on PATH",
|
|
4998
|
+
"Install or activate pnpm 11, then rerun forgeax init from the SDK root.",
|
|
4999
|
+
{ reason: cause instanceof Error ? cause.message : String(cause) }
|
|
5000
|
+
);
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
function matchesState(sdk, state) {
|
|
5004
|
+
if (state === null || typeof state !== "object" || Array.isArray(state)) return false;
|
|
5005
|
+
const value = state;
|
|
5006
|
+
return value.schemaVersion === SDK_INIT_SCHEMA_VERSION && value.sdkVersion === sdk.manifest.sdkVersion && value.engineCommit === sdk.manifest.engineCommit && typeof value.pnpm === "string" && supportedPnpm(value.pnpm) && value.node === process.versions.node && value.platform === process.platform && value.arch === process.arch;
|
|
5007
|
+
}
|
|
5008
|
+
async function readSdkInitState(sdk) {
|
|
5009
|
+
try {
|
|
5010
|
+
const value = JSON.parse(await readFile(sdkInitPath(sdk), "utf8"));
|
|
5011
|
+
return matchesState(sdk, value) ? value : void 0;
|
|
5012
|
+
} catch {
|
|
5013
|
+
return void 0;
|
|
5014
|
+
}
|
|
5015
|
+
}
|
|
5016
|
+
async function requireSdkInitialization(sdk) {
|
|
5017
|
+
const state = await readSdkInitState(sdk);
|
|
5018
|
+
if (state === void 0) {
|
|
5019
|
+
return commandFailure(
|
|
5020
|
+
"sdk-not-initialized",
|
|
5021
|
+
"the downloaded SDK to be initialized for this Node/pnpm/platform tuple",
|
|
5022
|
+
"Run ./bin/forgeax init from the SDK root once, then run forgeax new outside the SDK.",
|
|
5023
|
+
{
|
|
5024
|
+
sdkRoot: sdk.root,
|
|
5025
|
+
sdkVersion: sdk.manifest.sdkVersion,
|
|
5026
|
+
pnpm: sdk.manifest.requirements.pnpm,
|
|
5027
|
+
node: process.versions.node,
|
|
5028
|
+
platform: process.platform,
|
|
5029
|
+
arch: process.arch
|
|
5030
|
+
}
|
|
5031
|
+
);
|
|
5032
|
+
}
|
|
5033
|
+
return { ok: true, value: state };
|
|
5034
|
+
}
|
|
5035
|
+
async function copyBootstrapInputs(sdk, root2) {
|
|
5036
|
+
const template2 = sdk.templates.get(sdk.defaultTemplate);
|
|
5037
|
+
if (template2 === void 0) throw new Error("sdk-default-template-missing");
|
|
5038
|
+
for (const name of ["package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml"]) {
|
|
5039
|
+
await cp(resolve(template2, name), resolve(root2, name));
|
|
5040
|
+
}
|
|
5041
|
+
try {
|
|
5042
|
+
await cp(resolve(template2, ".npmrc"), resolve(root2, ".npmrc"));
|
|
5043
|
+
} catch (cause) {
|
|
5044
|
+
if (cause === null || typeof cause !== "object" || !("code" in cause) || cause.code !== "ENOENT") {
|
|
5045
|
+
throw cause;
|
|
5046
|
+
}
|
|
5047
|
+
}
|
|
5048
|
+
}
|
|
5049
|
+
async function sdkInitCommand(sdk, options = {}) {
|
|
5050
|
+
const pnpmResult = await currentPnpm();
|
|
5051
|
+
if (!pnpmResult.ok) return pnpmResult;
|
|
5052
|
+
if (!supportedPnpm(pnpmResult.value)) {
|
|
5053
|
+
return commandFailure(
|
|
5054
|
+
"pnpm-version-unsupported",
|
|
5055
|
+
"pnpm >=11.7.0 <12",
|
|
5056
|
+
"Activate pnpm 11.7.0 or newer in the pnpm 11 line, then rerun forgeax init.",
|
|
5057
|
+
{ actual: pnpmResult.value, expected: sdk.manifest.requirements.pnpm }
|
|
5058
|
+
);
|
|
5059
|
+
}
|
|
5060
|
+
const state = {
|
|
5061
|
+
schemaVersion: SDK_INIT_SCHEMA_VERSION,
|
|
5062
|
+
sdkVersion: sdk.manifest.sdkVersion,
|
|
5063
|
+
engineCommit: sdk.manifest.engineCommit,
|
|
5064
|
+
pnpm: pnpmResult.value,
|
|
5065
|
+
node: process.versions.node,
|
|
5066
|
+
platform: process.platform,
|
|
5067
|
+
arch: process.arch
|
|
5068
|
+
};
|
|
5069
|
+
const report = {
|
|
5070
|
+
...state,
|
|
5071
|
+
root: sdk.root,
|
|
5072
|
+
store: sdk.store === void 0 ? "registry" : "offline",
|
|
5073
|
+
onboarding: agentOnboarding(sdk)
|
|
5074
|
+
};
|
|
5075
|
+
if (options.dryRun === true || options.install === false) return { ok: true, value: report };
|
|
5076
|
+
let staging;
|
|
5077
|
+
try {
|
|
5078
|
+
staging = await mkdtemp(resolve(tmpdir(), "forgeax-sdk-init-"));
|
|
5079
|
+
await copyBootstrapInputs(sdk, staging);
|
|
5080
|
+
await execFileAsync("pnpm", sdkBootstrapInstallArgs(sdk.store), {
|
|
5081
|
+
cwd: staging,
|
|
5082
|
+
env: { ...process.env, CI: "true" },
|
|
5083
|
+
maxBuffer: 16 * 1024 * 1024
|
|
5084
|
+
});
|
|
5085
|
+
await mkdir(resolve(sdk.root, ".forgeax"), { recursive: true });
|
|
5086
|
+
await writeFile(sdkInitPath(sdk), `${JSON.stringify(state, null, 2)}
|
|
5087
|
+
`);
|
|
5088
|
+
return { ok: true, value: report };
|
|
5089
|
+
} catch (cause) {
|
|
5090
|
+
return commandFailure(
|
|
5091
|
+
"sdk-init-failed",
|
|
5092
|
+
"the SDK dependency closure to install and build native packages successfully",
|
|
5093
|
+
"Inspect the pnpm output, repair Node/pnpm or platform permissions, then rerun forgeax init from the SDK root.",
|
|
5094
|
+
{ reason: cause instanceof Error ? cause.message : String(cause), sdkRoot: sdk.root }
|
|
5095
|
+
);
|
|
5096
|
+
} finally {
|
|
5097
|
+
if (staging !== void 0) await rm(staging, { recursive: true, force: true });
|
|
5098
|
+
}
|
|
5099
|
+
}
|
|
5100
|
+
|
|
5101
|
+
// src/sdk-update.ts
|
|
5102
|
+
var UPDATE_MIGRATION_RISK = "Install a newer SDK in a separate directory. Existing games remain pinned and are not migrated automatically; review release notes and migrate and test each game before changing its Engine version.";
|
|
5103
|
+
function parseVersion(version) {
|
|
5104
|
+
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(
|
|
5105
|
+
version
|
|
5106
|
+
);
|
|
5107
|
+
if (match === null) return void 0;
|
|
5108
|
+
const major = match[1];
|
|
5109
|
+
const minor = match[2];
|
|
5110
|
+
const patch = match[3];
|
|
5111
|
+
if (major === void 0 || minor === void 0 || patch === void 0) return void 0;
|
|
5112
|
+
const prerelease = match[4]?.split(".");
|
|
5113
|
+
if (prerelease?.some(
|
|
5114
|
+
(identifier) => identifier.length === 0 || /^\d+$/.test(identifier) && /^0\d+/.test(identifier)
|
|
5115
|
+
)) {
|
|
5116
|
+
return void 0;
|
|
5117
|
+
}
|
|
5118
|
+
return {
|
|
5119
|
+
core: [major, minor, patch],
|
|
5120
|
+
...prerelease === void 0 ? {} : { prerelease }
|
|
5121
|
+
};
|
|
5122
|
+
}
|
|
5123
|
+
function compareNumericIdentifier(current, latest) {
|
|
5124
|
+
if (current.length !== latest.length) return latest.length > current.length ? 1 : -1;
|
|
5125
|
+
if (current === latest) return 0;
|
|
5126
|
+
return latest > current ? 1 : -1;
|
|
5127
|
+
}
|
|
5128
|
+
function comparePrerelease(current, latest) {
|
|
5129
|
+
const length = Math.max(current.length, latest.length);
|
|
5130
|
+
for (let index = 0; index < length; index += 1) {
|
|
5131
|
+
const currentIdentifier = current[index];
|
|
5132
|
+
const latestIdentifier = latest[index];
|
|
5133
|
+
if (currentIdentifier === void 0) return 1;
|
|
5134
|
+
if (latestIdentifier === void 0) return -1;
|
|
5135
|
+
if (currentIdentifier === latestIdentifier) continue;
|
|
5136
|
+
const currentNumeric = /^\d+$/.test(currentIdentifier);
|
|
5137
|
+
const latestNumeric = /^\d+$/.test(latestIdentifier);
|
|
5138
|
+
if (currentNumeric && latestNumeric) {
|
|
5139
|
+
return compareNumericIdentifier(currentIdentifier, latestIdentifier);
|
|
5140
|
+
}
|
|
5141
|
+
if (currentNumeric !== latestNumeric) return latestNumeric ? -1 : 1;
|
|
5142
|
+
return latestIdentifier > currentIdentifier ? 1 : -1;
|
|
5143
|
+
}
|
|
5144
|
+
return 0;
|
|
5145
|
+
}
|
|
5146
|
+
function newerSdkVersion(currentVersion, latestVersion) {
|
|
5147
|
+
const current = parseVersion(currentVersion);
|
|
5148
|
+
const latest = parseVersion(latestVersion);
|
|
5149
|
+
if (current === void 0 || latest === void 0) return false;
|
|
5150
|
+
for (let index = 0; index < current.core.length; index += 1) {
|
|
5151
|
+
const precedence = compareNumericIdentifier(
|
|
5152
|
+
current.core[index] ?? "0",
|
|
5153
|
+
latest.core[index] ?? "0"
|
|
5154
|
+
);
|
|
5155
|
+
if (precedence !== 0) return precedence > 0;
|
|
5156
|
+
}
|
|
5157
|
+
if (current.prerelease !== void 0 && latest.prerelease === void 0) return true;
|
|
5158
|
+
if (current.prerelease === void 0 || latest.prerelease === void 0) return false;
|
|
5159
|
+
return comparePrerelease(current.prerelease, latest.prerelease) > 0;
|
|
5160
|
+
}
|
|
5161
|
+
function offline() {
|
|
5162
|
+
return ["1", "true"].includes((process.env.npm_config_offline ?? "").toLowerCase());
|
|
5163
|
+
}
|
|
5164
|
+
async function checkSdkUpdate(currentVersion) {
|
|
5165
|
+
if (process.env.FORGEAX_DISABLE_UPDATE_CHECK === "1") {
|
|
5166
|
+
return { status: "skipped", currentVersion, reason: "disabled" };
|
|
5167
|
+
}
|
|
5168
|
+
if (offline()) return { status: "skipped", currentVersion, reason: "offline" };
|
|
5169
|
+
try {
|
|
5170
|
+
const registry = process.env.npm_config_registry ?? "https://registry.npmjs.org/";
|
|
5171
|
+
const response = await fetch(
|
|
5172
|
+
new URL("@forgeax%2Fengine-sdk", `${registry.replace(/\/$/, "")}/`),
|
|
5173
|
+
{
|
|
5174
|
+
headers: { accept: "application/vnd.npm.install-v1+json" },
|
|
5175
|
+
signal: AbortSignal.timeout(2e3)
|
|
5176
|
+
}
|
|
5177
|
+
);
|
|
5178
|
+
if (!response.ok) throw new Error(`registry-http-${response.status}`);
|
|
5179
|
+
const metadata = await response.json();
|
|
5180
|
+
const latestVersion = metadata["dist-tags"]?.latest;
|
|
5181
|
+
if (typeof latestVersion !== "string") throw new Error("registry-latest-version-missing");
|
|
5182
|
+
return newerSdkVersion(currentVersion, latestVersion) ? {
|
|
5183
|
+
status: "available",
|
|
5184
|
+
currentVersion,
|
|
5185
|
+
latestVersion,
|
|
5186
|
+
migrationRisk: UPDATE_MIGRATION_RISK
|
|
5187
|
+
} : { status: "current", currentVersion, latestVersion };
|
|
5188
|
+
} catch (cause) {
|
|
5189
|
+
return {
|
|
5190
|
+
status: "unavailable",
|
|
5191
|
+
currentVersion,
|
|
5192
|
+
reason: cause instanceof Error ? cause.message : String(cause)
|
|
5193
|
+
};
|
|
5194
|
+
}
|
|
5195
|
+
}
|
|
4958
5196
|
var PROJECT_SKILL_MOUNT_ROOTS = Object.freeze([
|
|
4959
5197
|
".codebuddy/skills",
|
|
4960
5198
|
".cursor/skills",
|
|
@@ -5246,7 +5484,7 @@ async function installProjectSkills(root2, provenance) {
|
|
|
5246
5484
|
}
|
|
5247
5485
|
|
|
5248
5486
|
// src/bootstrap-commands.ts
|
|
5249
|
-
var
|
|
5487
|
+
var execFileAsync2 = promisify(execFile);
|
|
5250
5488
|
function isMissingPathError(cause) {
|
|
5251
5489
|
return cause !== null && typeof cause === "object" && "code" in cause && cause.code === "ENOENT";
|
|
5252
5490
|
}
|
|
@@ -5265,6 +5503,20 @@ async function canonicalProspectivePath(path) {
|
|
|
5265
5503
|
}
|
|
5266
5504
|
}
|
|
5267
5505
|
}
|
|
5506
|
+
async function configureSdkStore(root2, store) {
|
|
5507
|
+
if (store === void 0) return;
|
|
5508
|
+
const path = resolve(root2, ".npmrc");
|
|
5509
|
+
let content = "";
|
|
5510
|
+
try {
|
|
5511
|
+
content = await readFile(path, "utf8");
|
|
5512
|
+
} catch (cause) {
|
|
5513
|
+
if (!isMissingPathError(cause)) throw cause;
|
|
5514
|
+
}
|
|
5515
|
+
const lines = content.split(/\r?\n/).filter((line) => line.trim().length > 0 && !/^\s*store-dir\s*=/.test(line));
|
|
5516
|
+
lines.push(`store-dir=${store}`);
|
|
5517
|
+
await writeFile(path, `${lines.join("\n")}
|
|
5518
|
+
`);
|
|
5519
|
+
}
|
|
5268
5520
|
function containsOrEquals(parent, child) {
|
|
5269
5521
|
const path = relative(parent, child);
|
|
5270
5522
|
return path === "" || path !== ".." && !path.startsWith(`..${sep}`) && !isAbsolute(path);
|
|
@@ -5275,25 +5527,14 @@ function nodeSupported() {
|
|
|
5275
5527
|
}
|
|
5276
5528
|
function pnpmSupported(version) {
|
|
5277
5529
|
const [major = 0, minor = 0] = version.split(".").map(Number);
|
|
5278
|
-
return major ===
|
|
5279
|
-
}
|
|
5280
|
-
function sdkInstallArgs(store) {
|
|
5281
|
-
if (store === void 0) return ["install", "--frozen-lockfile"];
|
|
5282
|
-
return [
|
|
5283
|
-
"install",
|
|
5284
|
-
"--offline",
|
|
5285
|
-
"--frozen-lockfile",
|
|
5286
|
-
"--side-effects-cache=false",
|
|
5287
|
-
"--store-dir",
|
|
5288
|
-
store
|
|
5289
|
-
];
|
|
5530
|
+
return major === 11 && minor >= 7;
|
|
5290
5531
|
}
|
|
5291
5532
|
async function doctorCommand(options = {}) {
|
|
5292
5533
|
const facts = await readProjectFacts(options.root);
|
|
5293
5534
|
if (!facts.ok) return facts;
|
|
5294
5535
|
let pnpm;
|
|
5295
5536
|
try {
|
|
5296
|
-
pnpm = (await
|
|
5537
|
+
pnpm = (await execFileAsync2("pnpm", ["--version"], { cwd: facts.value.root })).stdout.trim();
|
|
5297
5538
|
} catch (cause) {
|
|
5298
5539
|
return {
|
|
5299
5540
|
ok: false,
|
|
@@ -5321,7 +5562,7 @@ async function doctorCommand(options = {}) {
|
|
|
5321
5562
|
ok: false,
|
|
5322
5563
|
error: {
|
|
5323
5564
|
code: "pnpm-version-unsupported",
|
|
5324
|
-
expected: "pnpm >=
|
|
5565
|
+
expected: "pnpm >=11.7.0 <12",
|
|
5325
5566
|
hint: "Enable the packageManager-declared pnpm version with Corepack and retry.",
|
|
5326
5567
|
detail: { actual: pnpm }
|
|
5327
5568
|
}
|
|
@@ -5360,12 +5601,20 @@ async function doctorCommand(options = {}) {
|
|
|
5360
5601
|
};
|
|
5361
5602
|
}
|
|
5362
5603
|
async function initCommand(options = {}) {
|
|
5363
|
-
const facts = await readProjectFacts(options.root);
|
|
5364
|
-
if (!facts.ok) return facts;
|
|
5365
5604
|
try {
|
|
5366
5605
|
const sdk = await findSdkContext();
|
|
5606
|
+
const root2 = await canonicalProspectivePath(options.root ?? process.cwd());
|
|
5607
|
+
if (sdk !== void 0 && root2 === await canonicalProspectivePath(sdk.root)) {
|
|
5608
|
+
return sdkInitCommand(sdk, options);
|
|
5609
|
+
}
|
|
5610
|
+
const facts = await readProjectFacts(root2);
|
|
5611
|
+
if (!facts.ok) return facts;
|
|
5367
5612
|
const plan = createInitPlan(facts.value, sdk?.manifest);
|
|
5368
5613
|
if (!plan.ok) return plan;
|
|
5614
|
+
if (sdk !== void 0 && options.dryRun !== true) {
|
|
5615
|
+
const initialized = await requireSdkInitialization(sdk);
|
|
5616
|
+
if (!initialized.ok) return initialized;
|
|
5617
|
+
}
|
|
5369
5618
|
const applied = await applyInitPlan(facts.value, plan.value, options);
|
|
5370
5619
|
if (!applied.ok || options.dryRun === true) return applied;
|
|
5371
5620
|
if (sdk !== void 0) {
|
|
@@ -5375,12 +5624,31 @@ async function initCommand(options = {}) {
|
|
|
5375
5624
|
resolve(template2, "pnpm-lock.yaml"),
|
|
5376
5625
|
resolve(facts.value.root, "pnpm-lock.yaml")
|
|
5377
5626
|
);
|
|
5627
|
+
await copyFile(
|
|
5628
|
+
resolve(template2, "pnpm-workspace.yaml"),
|
|
5629
|
+
resolve(facts.value.root, "pnpm-workspace.yaml")
|
|
5630
|
+
);
|
|
5631
|
+
try {
|
|
5632
|
+
await readFile(resolve(facts.value.root, ".npmrc"), "utf8");
|
|
5633
|
+
} catch (cause) {
|
|
5634
|
+
if (!isMissingPathError(cause)) throw cause;
|
|
5635
|
+
try {
|
|
5636
|
+
await copyFile(resolve(template2, ".npmrc"), resolve(facts.value.root, ".npmrc"));
|
|
5637
|
+
} catch (templateCause) {
|
|
5638
|
+
if (!isMissingPathError(templateCause)) throw templateCause;
|
|
5639
|
+
}
|
|
5640
|
+
}
|
|
5378
5641
|
await copySdkSkills(sdk, facts.value.root);
|
|
5379
5642
|
await installProjectSkills(facts.value.root, sdk.manifest);
|
|
5643
|
+
await configureSdkStore(
|
|
5644
|
+
facts.value.root,
|
|
5645
|
+
sdk.store === void 0 ? void 0 : await canonicalProspectivePath(sdk.store)
|
|
5646
|
+
);
|
|
5380
5647
|
}
|
|
5381
5648
|
if (options.install === false) return applied;
|
|
5382
|
-
const
|
|
5383
|
-
|
|
5649
|
+
const store = sdk === void 0 || sdk.store === void 0 ? void 0 : await canonicalProspectivePath(sdk.store);
|
|
5650
|
+
const installArgs = sdk === void 0 ? ["install", "--frozen-lockfile=false"] : sdkProjectInstallArgs(store);
|
|
5651
|
+
await execFileAsync2("pnpm", installArgs, {
|
|
5384
5652
|
cwd: facts.value.root,
|
|
5385
5653
|
env: { ...process.env, CI: "true" },
|
|
5386
5654
|
maxBuffer: 16 * 1024 * 1024
|
|
@@ -5458,12 +5726,15 @@ async function newCommand(options = {}) {
|
|
|
5458
5726
|
value: { root: root2, template: templateId, sdkVersion: sdk.manifest.sdkVersion }
|
|
5459
5727
|
};
|
|
5460
5728
|
}
|
|
5729
|
+
const initialized = await requireSdkInitialization(sdk);
|
|
5730
|
+
if (!initialized.ok) return initialized;
|
|
5461
5731
|
const parent = dirname(root2);
|
|
5462
5732
|
await mkdir(parent, { recursive: true });
|
|
5463
5733
|
let staging = await mkdtemp(
|
|
5464
5734
|
resolve(parent, `.${basename(root2)}.forgeax-staging-`)
|
|
5465
5735
|
);
|
|
5466
5736
|
const committedNames = [];
|
|
5737
|
+
let committed = false;
|
|
5467
5738
|
try {
|
|
5468
5739
|
for (const name of await readdir(template2)) {
|
|
5469
5740
|
await cp(resolve(template2, name), resolve(staging, name), {
|
|
@@ -5474,14 +5745,12 @@ async function newCommand(options = {}) {
|
|
|
5474
5745
|
}
|
|
5475
5746
|
await copySdkSkills(sdk, staging);
|
|
5476
5747
|
await installProjectSkills(staging, sdk.manifest);
|
|
5477
|
-
await
|
|
5478
|
-
|
|
5479
|
-
env: { ...process.env, CI: "true" },
|
|
5480
|
-
maxBuffer: 16 * 1024 * 1024
|
|
5481
|
-
});
|
|
5748
|
+
const store = sdk.store === void 0 ? void 0 : await canonicalProspectivePath(sdk.store);
|
|
5749
|
+
await configureSdkStore(staging, store);
|
|
5482
5750
|
if (!targetExists) {
|
|
5483
5751
|
await rename(staging, root2);
|
|
5484
5752
|
staging = void 0;
|
|
5753
|
+
committedNames.push(...await readdir(root2));
|
|
5485
5754
|
} else {
|
|
5486
5755
|
for (const name of await readdir(staging)) {
|
|
5487
5756
|
await rename(resolve(staging, name), resolve(root2, name));
|
|
@@ -5490,15 +5759,41 @@ async function newCommand(options = {}) {
|
|
|
5490
5759
|
await rm(staging, { recursive: true, force: true });
|
|
5491
5760
|
staging = void 0;
|
|
5492
5761
|
}
|
|
5762
|
+
committed = true;
|
|
5763
|
+
await execFileAsync2("pnpm", sdkProjectInstallArgs(store), {
|
|
5764
|
+
cwd: root2,
|
|
5765
|
+
env: { ...process.env, CI: "true" },
|
|
5766
|
+
maxBuffer: 16 * 1024 * 1024
|
|
5767
|
+
});
|
|
5768
|
+
const sdkUpdate = await checkSdkUpdate(sdk.manifest.sdkVersion);
|
|
5493
5769
|
return {
|
|
5494
5770
|
ok: true,
|
|
5495
|
-
value: {
|
|
5771
|
+
value: {
|
|
5772
|
+
root: root2,
|
|
5773
|
+
template: templateId,
|
|
5774
|
+
sdkVersion: sdk.manifest.sdkVersion,
|
|
5775
|
+
onboarding: agentOnboarding(sdk, root2),
|
|
5776
|
+
sdkUpdate
|
|
5777
|
+
}
|
|
5496
5778
|
};
|
|
5497
5779
|
} catch (cause) {
|
|
5498
5780
|
if (staging !== void 0) await rm(staging, { recursive: true, force: true });
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5781
|
+
if (committed) {
|
|
5782
|
+
if (!targetExists) {
|
|
5783
|
+
await rm(root2, { recursive: true, force: true });
|
|
5784
|
+
return { ok: false, error: commandError(cause, "project-create-failed") };
|
|
5785
|
+
}
|
|
5786
|
+
const cleanupNames = /* @__PURE__ */ new Set([...committedNames, "node_modules"]);
|
|
5787
|
+
await Promise.all(
|
|
5788
|
+
[...cleanupNames].map(
|
|
5789
|
+
(name) => rm(resolve(root2, name), { recursive: true, force: true })
|
|
5790
|
+
)
|
|
5791
|
+
);
|
|
5792
|
+
} else {
|
|
5793
|
+
await Promise.all(
|
|
5794
|
+
committedNames.map((name) => rm(resolve(root2, name), { recursive: true, force: true }))
|
|
5795
|
+
);
|
|
5796
|
+
}
|
|
5502
5797
|
return { ok: false, error: commandError(cause, "project-create-failed") };
|
|
5503
5798
|
}
|
|
5504
5799
|
} catch (cause) {
|
|
@@ -5506,11 +5801,42 @@ async function newCommand(options = {}) {
|
|
|
5506
5801
|
}
|
|
5507
5802
|
}
|
|
5508
5803
|
|
|
5804
|
+
// src/cli-output.ts
|
|
5805
|
+
function agentOnboardingLines(value) {
|
|
5806
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return [];
|
|
5807
|
+
const onboarding = value.onboarding;
|
|
5808
|
+
if (onboarding === null || typeof onboarding !== "object" || Array.isArray(onboarding)) return [];
|
|
5809
|
+
const candidate = onboarding;
|
|
5810
|
+
const lines = Array.isArray(candidate.read) ? candidate.read.filter((path) => typeof path === "string").map((path) => `[forgeax] read: ${path}`) : [];
|
|
5811
|
+
if (typeof candidate.next?.cwd === "string" && Array.isArray(candidate.next.argv) && candidate.next.argv.every((part) => typeof part === "string")) {
|
|
5812
|
+
lines.push(`[forgeax] next cwd: ${candidate.next.cwd}`);
|
|
5813
|
+
lines.push(`[forgeax] next: ${candidate.next.argv.join(" ")}`);
|
|
5814
|
+
}
|
|
5815
|
+
return lines;
|
|
5816
|
+
}
|
|
5817
|
+
function sdkUpdateLines(value) {
|
|
5818
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return [];
|
|
5819
|
+
const update = value.sdkUpdate;
|
|
5820
|
+
if (update === null || typeof update !== "object" || Array.isArray(update)) return [];
|
|
5821
|
+
const candidate = update;
|
|
5822
|
+
if (candidate.status !== "available" || typeof candidate.currentVersion !== "string" || typeof candidate.latestVersion !== "string" || typeof candidate.migrationRisk !== "string") {
|
|
5823
|
+
return [];
|
|
5824
|
+
}
|
|
5825
|
+
return [
|
|
5826
|
+
`[forgeax] update available: @forgeax/engine-sdk ${candidate.currentVersion} -> ${candidate.latestVersion}`,
|
|
5827
|
+
`[forgeax] update note: ${candidate.migrationRisk}`
|
|
5828
|
+
];
|
|
5829
|
+
}
|
|
5830
|
+
function renderForgeaxUsage() {
|
|
5831
|
+
return "Usage: forgeax new [directory] [--template empty|game-default]\n forgeax <init|doctor|test|dev|build|package|serve|preview> [directory]\n forgeax package [directory] [--output release/game-web.zip]\n forgeax run <rhi.capture|rhi.summary|rhi.inspect> [options]\n forgeax run <operation-id> --input <request.json>\n forgeax asset <add|verify|inspect|list> [subject]\n forgeax shader check [path]\n forgeax plugin <install|uninstall> <module-or-id> [options]\n forgeax skill <install|verify> [--root directory]\n forgeax sdk install <directory> [--version VERSION]\n";
|
|
5832
|
+
}
|
|
5833
|
+
|
|
5509
5834
|
// src/sdk-cli.ts
|
|
5510
|
-
var
|
|
5835
|
+
var rawArgs = process.argv.slice(2);
|
|
5836
|
+
var args = [...rawArgs];
|
|
5511
5837
|
var commandArg = args.shift();
|
|
5512
5838
|
var command = commandArg;
|
|
5513
|
-
var help =
|
|
5839
|
+
var help = rawArgs.includes("--help") || rawArgs.includes("-h");
|
|
5514
5840
|
var json = args.includes("--json");
|
|
5515
5841
|
var dryRun = args.includes("--dry-run");
|
|
5516
5842
|
var noInstall = args.includes("--no-install");
|
|
@@ -5520,23 +5846,43 @@ var positional = args.find(
|
|
|
5520
5846
|
(value, index) => !value.startsWith("--") && (templateIndex < 0 || index !== templateIndex + 1)
|
|
5521
5847
|
);
|
|
5522
5848
|
var root = command === "new" || command === "init" || command === "doctor" ? positional ?? process.cwd() : process.cwd();
|
|
5523
|
-
if (
|
|
5849
|
+
if (help) {
|
|
5850
|
+
process.stdout.write(renderForgeaxUsage());
|
|
5851
|
+
} else if (command === "new" || command === "init" || command === "doctor") {
|
|
5524
5852
|
const result = await (command === "new" ? newCommand({ root, dryRun, ...template === void 0 ? {} : { template } }) : command === "init" ? initCommand({ root, dryRun, install: !noInstall }) : doctorCommand({ root}));
|
|
5525
5853
|
const envelope = result.ok ? { schemaVersion: "1.0.0", command, ok: true, value: result.value } : { schemaVersion: "1.0.0", command, ok: false, error: result.error };
|
|
5526
5854
|
if (json) process.stdout.write(`${JSON.stringify(envelope)}
|
|
5527
5855
|
`);
|
|
5528
|
-
else if (result.ok)
|
|
5856
|
+
else if (result.ok) {
|
|
5857
|
+
process.stdout.write(`[forgeax] ${command} ready
|
|
5858
|
+
`);
|
|
5859
|
+
for (const line of agentOnboardingLines(result.value)) process.stdout.write(`${line}
|
|
5860
|
+
`);
|
|
5861
|
+
for (const line of sdkUpdateLines(result.value)) process.stdout.write(`${line}
|
|
5529
5862
|
`);
|
|
5530
|
-
else process.stderr.write(`[forgeax] ${result.error.code}: ${result.error.hint}
|
|
5863
|
+
} else process.stderr.write(`[forgeax] ${result.error.code}: ${result.error.hint}
|
|
5531
5864
|
`);
|
|
5532
5865
|
if (!result.ok) process.exitCode = 1;
|
|
5533
|
-
} else if (command !== void 0
|
|
5534
|
-
const
|
|
5535
|
-
|
|
5866
|
+
} else if (command !== void 0) {
|
|
5867
|
+
const localForgeax = resolve(
|
|
5868
|
+
root,
|
|
5869
|
+
"node_modules",
|
|
5870
|
+
".bin",
|
|
5871
|
+
process.platform === "win32" ? "forgeax.cmd" : "forgeax"
|
|
5872
|
+
);
|
|
5873
|
+
const child = spawn(localForgeax, [command, ...args], {
|
|
5874
|
+
cwd: root,
|
|
5875
|
+
shell: process.platform === "win32",
|
|
5876
|
+
stdio: "inherit"
|
|
5877
|
+
});
|
|
5878
|
+
const exitCode = await new Promise((resolveExit, reject) => {
|
|
5879
|
+
child.once("error", reject);
|
|
5880
|
+
child.once("exit", resolveExit);
|
|
5881
|
+
});
|
|
5882
|
+
if (exitCode !== 0) process.exitCode = exitCode ?? 1;
|
|
5536
5883
|
} else {
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
if (!help) process.exitCode = 2;
|
|
5884
|
+
process.stderr.write(renderForgeaxUsage());
|
|
5885
|
+
process.exitCode = 2;
|
|
5540
5886
|
}
|
|
5541
5887
|
/*! Bundled license information:
|
|
5542
5888
|
|