@forgeax/game 0.3.2 → 0.3.4
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 +56 -24
- package/assets/asset3d/provider/asset3d-search-provider-c181c48fbffc933a7ce9a0836f7878ca5e6d77e1-darwin-arm64.tar.gz +0 -0
- package/assets/asset3d/provider/asset3d-search-provider-c181c48fbffc933a7ce9a0836f7878ca5e6d77e1-linux-x64.tar.gz +0 -0
- package/assets/asset3d/vibegame-art-3d-asset-library-2.0.0.tgz +0 -0
- package/assets/asset3d/vibegame-art-3d-asset-library-2.0.0.tgz.sha256 +1 -0
- package/dist/main.js +600 -66
- package/docs/asset3d.md +44 -28
- package/docs/release-0.3.3.md +73 -0
- package/docs/release-0.3.4.md +66 -0
- package/package.json +4 -1
package/dist/main.js
CHANGED
|
@@ -2946,7 +2946,7 @@ var RELEASE_IDENTITY_MIME = "application/vnd.forgeax.game-release-identity+json"
|
|
|
2946
2946
|
var RELEASE_IDENTITY = Object.freeze({
|
|
2947
2947
|
schema: RELEASE_IDENTITY_SCHEMA,
|
|
2948
2948
|
gamePackage: "@forgeax/game",
|
|
2949
|
-
gameVersion: "0.3.
|
|
2949
|
+
gameVersion: "0.3.4",
|
|
2950
2950
|
gameBin: "forgeax-game",
|
|
2951
2951
|
engineSdkPackage: ENGINE_SDK_PACKAGE,
|
|
2952
2952
|
engineSdkVersion: ENGINE_VERSION,
|
|
@@ -3349,13 +3349,24 @@ function findClient(id) {
|
|
|
3349
3349
|
return CLIENTS.find((client) => client.id === id || client.aliases?.includes(id));
|
|
3350
3350
|
}
|
|
3351
3351
|
var SERVER_KEY = "forgeax";
|
|
3352
|
+
var ASSET3D_SERVER_KEY = "asset3d-search";
|
|
3352
3353
|
function launchSpec(mode) {
|
|
3353
3354
|
if (mode === "local") {
|
|
3354
3355
|
return { command: process.execPath, args: [resolve9(process.argv[1] ?? ""), "mcp"] };
|
|
3355
3356
|
}
|
|
3356
3357
|
return {
|
|
3357
3358
|
command: "npx",
|
|
3358
|
-
args: ["-y", "-p", "@forgeax/game@0.3.
|
|
3359
|
+
args: ["-y", "-p", "@forgeax/game@0.3.4", "forgeax-game", "mcp"]
|
|
3360
|
+
};
|
|
3361
|
+
}
|
|
3362
|
+
function asset3dLaunchSpec(mode) {
|
|
3363
|
+
const launch = launchSpec(mode);
|
|
3364
|
+
if (launch.args.at(-1) !== "mcp") {
|
|
3365
|
+
throw new Error("asset3d_game_plugin_launch_invalid: expected MCP launch suffix");
|
|
3366
|
+
}
|
|
3367
|
+
return {
|
|
3368
|
+
command: launch.command,
|
|
3369
|
+
args: [...launch.args.slice(0, -1), "asset3d", "mcp"]
|
|
3359
3370
|
};
|
|
3360
3371
|
}
|
|
3361
3372
|
|
|
@@ -3494,6 +3505,16 @@ ${suffix ? `
|
|
|
3494
3505
|
function hasTomlTable(content, header) {
|
|
3495
3506
|
return [...content.matchAll(HEADER_RE)].some((match) => sameHeader(match[1], header));
|
|
3496
3507
|
}
|
|
3508
|
+
function readTomlTable(content, header) {
|
|
3509
|
+
const headerLines = [...content.matchAll(HEADER_RE)];
|
|
3510
|
+
const ownedIndexes = headerLines.flatMap((match, index) => sameHeader(match[1], header) ? [index] : []);
|
|
3511
|
+
if (ownedIndexes.length !== 1)
|
|
3512
|
+
return;
|
|
3513
|
+
const ownedIndex = ownedIndexes[0];
|
|
3514
|
+
const owned = headerLines[ownedIndex];
|
|
3515
|
+
const next = headerLines[ownedIndex + 1];
|
|
3516
|
+
return content.slice(owned.index, next?.index ?? content.length);
|
|
3517
|
+
}
|
|
3497
3518
|
function hasCompetingTomlDefinition(content, header) {
|
|
3498
3519
|
const headerLines = [...content.matchAll(HEADER_RE)];
|
|
3499
3520
|
return hasCompetingInlineOwner(content, header, headerLines);
|
|
@@ -3794,20 +3815,57 @@ function validateTomlSyntax(source) {
|
|
|
3794
3815
|
if (brackets.length)
|
|
3795
3816
|
throw new Error("unterminated TOML array or inline table");
|
|
3796
3817
|
}
|
|
3797
|
-
function
|
|
3818
|
+
function validTomlTableHeader(line) {
|
|
3819
|
+
const arrayTable = line.startsWith("[[");
|
|
3820
|
+
const openingWidth = arrayTable ? 2 : 1;
|
|
3821
|
+
let quote;
|
|
3822
|
+
for (let index = openingWidth;index < line.length; index++) {
|
|
3823
|
+
const char = line[index];
|
|
3824
|
+
if (quote === "basic") {
|
|
3825
|
+
if (char === "\\") {
|
|
3826
|
+
index++;
|
|
3827
|
+
continue;
|
|
3828
|
+
}
|
|
3829
|
+
if (char === '"')
|
|
3830
|
+
quote = undefined;
|
|
3831
|
+
continue;
|
|
3832
|
+
}
|
|
3833
|
+
if (quote === "literal") {
|
|
3834
|
+
if (char === "'")
|
|
3835
|
+
quote = undefined;
|
|
3836
|
+
continue;
|
|
3837
|
+
}
|
|
3838
|
+
if (char === '"') {
|
|
3839
|
+
quote = "basic";
|
|
3840
|
+
continue;
|
|
3841
|
+
}
|
|
3842
|
+
if (char === "'") {
|
|
3843
|
+
quote = "literal";
|
|
3844
|
+
continue;
|
|
3845
|
+
}
|
|
3846
|
+
const closes = arrayTable ? line.startsWith("]]", index) : char === "]";
|
|
3847
|
+
if (!closes)
|
|
3848
|
+
continue;
|
|
3849
|
+
const body = line.slice(openingWidth, index).trim();
|
|
3850
|
+
const suffix = line.slice(index + openingWidth);
|
|
3851
|
+
return body.length > 0 && /^[ \t]*(?:#.*)?$/.test(suffix);
|
|
3852
|
+
}
|
|
3853
|
+
return false;
|
|
3854
|
+
}
|
|
3855
|
+
function mergeJsonConfig(existing, spec, entry, serverKey = SERVER_KEY) {
|
|
3798
3856
|
const path = spec.path("");
|
|
3799
3857
|
const mapKey = spec.serverMapKey ?? ["mcpServers"];
|
|
3800
3858
|
const wanted = JSON.stringify(entry);
|
|
3801
3859
|
if (!existing || existing.trim() === "") {
|
|
3802
3860
|
if (existing && JSON_WHITESPACE.test(existing)) {
|
|
3803
|
-
let nested2 = { [
|
|
3861
|
+
let nested2 = { [serverKey]: entry };
|
|
3804
3862
|
for (let index = mapKey.length - 1;index >= 0; index--) {
|
|
3805
3863
|
nested2 = { [mapKey[index]]: nested2 };
|
|
3806
3864
|
}
|
|
3807
3865
|
return { content: `${existing}${JSON.stringify(nested2)}
|
|
3808
3866
|
`, changed: true };
|
|
3809
3867
|
}
|
|
3810
|
-
let nested = { [
|
|
3868
|
+
let nested = { [serverKey]: entry };
|
|
3811
3869
|
for (let index = mapKey.length - 1;index >= 0; index--) {
|
|
3812
3870
|
nested = { [mapKey[index]]: nested };
|
|
3813
3871
|
}
|
|
@@ -3822,7 +3880,7 @@ function mergeJsonConfig(existing, spec, entry) {
|
|
|
3822
3880
|
const key = mapKey[index];
|
|
3823
3881
|
const found = jsonMember(members, key, path);
|
|
3824
3882
|
if (!found) {
|
|
3825
|
-
let nested = { [
|
|
3883
|
+
let nested = { [serverKey]: entry };
|
|
3826
3884
|
for (let nestedIndex = mapKey.length - 1;nestedIndex >= index; nestedIndex--) {
|
|
3827
3885
|
nested = { [mapKey[nestedIndex]]: nested };
|
|
3828
3886
|
}
|
|
@@ -3836,29 +3894,29 @@ function mergeJsonConfig(existing, spec, entry) {
|
|
|
3836
3894
|
members = nestedMembers;
|
|
3837
3895
|
}
|
|
3838
3896
|
const mapMembers = members;
|
|
3839
|
-
const owned = jsonMember(mapMembers,
|
|
3897
|
+
const owned = jsonMember(mapMembers, serverKey, `${path}.${mapKey.join(".")}`);
|
|
3840
3898
|
if (owned) {
|
|
3841
3899
|
let before;
|
|
3842
3900
|
try {
|
|
3843
3901
|
before = JSON.parse(source.slice(owned.value.start, owned.value.end));
|
|
3844
3902
|
} catch {
|
|
3845
|
-
throw new Error(`${path}.mcpServers.${
|
|
3903
|
+
throw new Error(`${path}.mcpServers.${serverKey} is not valid JSON`);
|
|
3846
3904
|
}
|
|
3847
3905
|
if (JSON.stringify(before) === wanted)
|
|
3848
3906
|
return { content: existing, changed: false };
|
|
3849
3907
|
return { content: replaceJsonValue(source, owned.value, wanted), changed: true };
|
|
3850
3908
|
}
|
|
3851
3909
|
return {
|
|
3852
|
-
content: appendJsonMember(source, container, mapMembers,
|
|
3910
|
+
content: appendJsonMember(source, container, mapMembers, serverKey, wanted),
|
|
3853
3911
|
changed: true
|
|
3854
3912
|
};
|
|
3855
3913
|
}
|
|
3856
|
-
function mergeTomlConfig(existing, entry) {
|
|
3914
|
+
function mergeTomlConfig(existing, entry, serverKey = SERVER_KEY) {
|
|
3857
3915
|
if (existing !== undefined && existing.trim() !== "") {
|
|
3858
3916
|
validateTomlSyntax(existing);
|
|
3859
3917
|
for (const [index, line] of existing.split(/\r?\n/).entries()) {
|
|
3860
3918
|
const trimmed = line.trim();
|
|
3861
|
-
if (trimmed.startsWith("[") &&
|
|
3919
|
+
if (trimmed.startsWith("[") && !validTomlTableHeader(trimmed)) {
|
|
3862
3920
|
throw new Error(`line ${index + 1} has an invalid TOML table header`);
|
|
3863
3921
|
}
|
|
3864
3922
|
}
|
|
@@ -3870,10 +3928,47 @@ function mergeTomlConfig(existing, entry) {
|
|
|
3870
3928
|
const args = entry.args;
|
|
3871
3929
|
if (Array.isArray(args))
|
|
3872
3930
|
body.push(`args = ${encodeTomlStringArray(args)}`);
|
|
3873
|
-
const content = upsertTomlTable(existing ?? "", { header: `mcp_servers.${
|
|
3931
|
+
const content = upsertTomlTable(existing ?? "", { header: `mcp_servers.${serverKey}`, body });
|
|
3874
3932
|
return { content, changed: content !== (existing ?? "") };
|
|
3875
3933
|
}
|
|
3876
|
-
function
|
|
3934
|
+
function jsonServerEntry(parsed, spec, serverKey) {
|
|
3935
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
3936
|
+
return;
|
|
3937
|
+
let cursor = parsed;
|
|
3938
|
+
for (const key of spec.serverMapKey ?? ["mcpServers"]) {
|
|
3939
|
+
if (typeof cursor !== "object" || cursor === null || Array.isArray(cursor))
|
|
3940
|
+
return;
|
|
3941
|
+
cursor = cursor[key];
|
|
3942
|
+
}
|
|
3943
|
+
if (typeof cursor !== "object" || cursor === null || Array.isArray(cursor))
|
|
3944
|
+
return;
|
|
3945
|
+
return cursor[serverKey];
|
|
3946
|
+
}
|
|
3947
|
+
function gameVersionFromEntryText(entry) {
|
|
3948
|
+
const pinned = entry.match(/@forgeax\/game@([0-9A-Za-z][0-9A-Za-z.+_-]*)/);
|
|
3949
|
+
if (pinned)
|
|
3950
|
+
return pinned[1];
|
|
3951
|
+
if (entry.includes("@forgeax/game"))
|
|
3952
|
+
return "unversioned";
|
|
3953
|
+
return "local/custom";
|
|
3954
|
+
}
|
|
3955
|
+
function configuredGameVersion(spec, projectRoot, serverKey = SERVER_KEY) {
|
|
3956
|
+
const path = spec.path(projectRoot);
|
|
3957
|
+
if (!existsSync7(path))
|
|
3958
|
+
return;
|
|
3959
|
+
try {
|
|
3960
|
+
const existing = readFileSync11(path, "utf8");
|
|
3961
|
+
if (spec.format === "toml") {
|
|
3962
|
+
const table = readTomlTable(existing, `mcp_servers.${serverKey}`);
|
|
3963
|
+
return table === undefined ? undefined : gameVersionFromEntryText(table);
|
|
3964
|
+
}
|
|
3965
|
+
const entry = jsonServerEntry(JSON.parse(existing), spec, serverKey);
|
|
3966
|
+
return entry === undefined ? undefined : gameVersionFromEntryText(JSON.stringify(entry));
|
|
3967
|
+
} catch {
|
|
3968
|
+
return;
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
function inspectConfig(spec, projectRoot, launch, serverKey = SERVER_KEY) {
|
|
3877
3972
|
const path = spec.path(projectRoot);
|
|
3878
3973
|
if (!existsSync7(path))
|
|
3879
3974
|
return { path, state: "missing" };
|
|
@@ -3881,7 +3976,7 @@ function inspectConfig(spec, projectRoot, launch) {
|
|
|
3881
3976
|
try {
|
|
3882
3977
|
existing = readFileSync11(path, "utf8");
|
|
3883
3978
|
if (spec.format === "toml") {
|
|
3884
|
-
const header = `mcp_servers.${
|
|
3979
|
+
const header = `mcp_servers.${serverKey}`;
|
|
3885
3980
|
if (!hasTomlTable(existing, header)) {
|
|
3886
3981
|
if (hasCompetingTomlDefinition(existing, header)) {
|
|
3887
3982
|
return {
|
|
@@ -3894,24 +3989,14 @@ function inspectConfig(spec, projectRoot, launch) {
|
|
|
3894
3989
|
}
|
|
3895
3990
|
return {
|
|
3896
3991
|
path,
|
|
3897
|
-
state: mergeTomlConfig(existing, buildEntry(spec, launch)).changed ? "different" : "current"
|
|
3992
|
+
state: mergeTomlConfig(existing, buildEntry(spec, launch), serverKey).changed ? "different" : "current"
|
|
3898
3993
|
};
|
|
3899
3994
|
}
|
|
3900
3995
|
const parsed = JSON.parse(existing);
|
|
3901
3996
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
3902
3997
|
return { path, state: "invalid", detail: "top level is not a JSON object" };
|
|
3903
3998
|
}
|
|
3904
|
-
|
|
3905
|
-
for (const key of spec.serverMapKey ?? ["mcpServers"]) {
|
|
3906
|
-
if (typeof cursor !== "object" || cursor === null || Array.isArray(cursor)) {
|
|
3907
|
-
return { path, state: "not_configured" };
|
|
3908
|
-
}
|
|
3909
|
-
cursor = cursor[key];
|
|
3910
|
-
}
|
|
3911
|
-
if (typeof cursor !== "object" || cursor === null || Array.isArray(cursor)) {
|
|
3912
|
-
return { path, state: "not_configured" };
|
|
3913
|
-
}
|
|
3914
|
-
const entry = cursor[SERVER_KEY];
|
|
3999
|
+
const entry = jsonServerEntry(parsed, spec, serverKey);
|
|
3915
4000
|
if (entry === undefined)
|
|
3916
4001
|
return { path, state: "not_configured" };
|
|
3917
4002
|
return {
|
|
@@ -3922,11 +4007,11 @@ function inspectConfig(spec, projectRoot, launch) {
|
|
|
3922
4007
|
return { path, state: "invalid", detail: error instanceof Error ? error.message : String(error) };
|
|
3923
4008
|
}
|
|
3924
4009
|
}
|
|
3925
|
-
function applyConfig(spec, projectRoot, launch) {
|
|
4010
|
+
function applyConfig(spec, projectRoot, launch, serverKey = SERVER_KEY) {
|
|
3926
4011
|
const path = spec.path(projectRoot);
|
|
3927
4012
|
const existing = existsSync7(path) ? readFileSync11(path, "utf8") : undefined;
|
|
3928
4013
|
const entry = buildEntry(spec, launch);
|
|
3929
|
-
const merged = spec.format === "toml" ? mergeTomlConfig(existing, entry) : mergeJsonConfig(existing, spec, entry);
|
|
4014
|
+
const merged = spec.format === "toml" ? mergeTomlConfig(existing, entry, serverKey) : mergeJsonConfig(existing, spec, entry, serverKey);
|
|
3930
4015
|
if (!merged.changed)
|
|
3931
4016
|
return { path, changed: false };
|
|
3932
4017
|
mkdirSync7(dirname8(path), { recursive: true });
|
|
@@ -3938,14 +4023,14 @@ function applyConfig(spec, projectRoot, launch) {
|
|
|
3938
4023
|
writeFileSync7(path, merged.content);
|
|
3939
4024
|
return { path, changed: true, ...backup ? { backup } : {} };
|
|
3940
4025
|
}
|
|
3941
|
-
function removeConfig(spec, projectRoot) {
|
|
4026
|
+
function removeConfig(spec, projectRoot, serverKey = SERVER_KEY) {
|
|
3942
4027
|
const path = spec.path(projectRoot);
|
|
3943
4028
|
if (!existsSync7(path))
|
|
3944
4029
|
return { path, changed: false };
|
|
3945
4030
|
const existing = readFileSync11(path, "utf8");
|
|
3946
4031
|
let content;
|
|
3947
4032
|
if (spec.format === "toml") {
|
|
3948
|
-
content = removeTomlTable(existing, `mcp_servers.${
|
|
4033
|
+
content = removeTomlTable(existing, `mcp_servers.${serverKey}`);
|
|
3949
4034
|
} else {
|
|
3950
4035
|
let parsed;
|
|
3951
4036
|
try {
|
|
@@ -3960,9 +4045,9 @@ function removeConfig(spec, projectRoot) {
|
|
|
3960
4045
|
return { path, changed: false };
|
|
3961
4046
|
cursor = next;
|
|
3962
4047
|
}
|
|
3963
|
-
if (!(
|
|
4048
|
+
if (!(serverKey in cursor))
|
|
3964
4049
|
return { path, changed: false };
|
|
3965
|
-
delete cursor[
|
|
4050
|
+
delete cursor[serverKey];
|
|
3966
4051
|
content = `${JSON.stringify(parsed, null, 2)}
|
|
3967
4052
|
`;
|
|
3968
4053
|
}
|
|
@@ -4184,14 +4269,18 @@ import { createHash as createHash6 } from "node:crypto";
|
|
|
4184
4269
|
var PROVIDER_BUNDLE_SCHEMA = "forgeax.asset3d-provider-bundle/1.0.0";
|
|
4185
4270
|
var PROVIDER_RESULT_SCHEMA = "forgeax.asset3d-search-result/1.0.0";
|
|
4186
4271
|
var PROVIDER_RECEIPT_SCHEMA = "forgeax.asset3d-search-receipt/1.0.0";
|
|
4187
|
-
var K0_PACKAGE_SHA256 = "
|
|
4272
|
+
var K0_PACKAGE_SHA256 = "4937fe00e2b919319c70c82db53ebe359b275ed662c026153ecb97f7601262b4";
|
|
4188
4273
|
var RESULT_SCHEMA_SHA256 = "ab6e5e1e794d5428efef362fe32d4c54b6541e12539bae5f6ff27680c9f380ba";
|
|
4189
4274
|
var RECEIPT_SCHEMA_SHA256 = "e0dc9e9fe9872f09af9fca6d0c17d22aca63c2b546c55c0b669c581f7059b9c4";
|
|
4190
4275
|
var ASSET3D_PROVIDER_COMMIT = "c181c48fbffc933a7ce9a0836f7878ca5e6d77e1";
|
|
4191
4276
|
var BUNDLED_ASSET3D_PROVIDERS = Object.freeze({
|
|
4192
4277
|
"darwin-arm64": {
|
|
4193
|
-
sha256: "
|
|
4278
|
+
sha256: "9492c507a3afe5cafcd50b5f782b313c8716c2e52f5def06592c6a100c0459db",
|
|
4194
4279
|
relativePath: "asset3d/provider/asset3d-search-provider-c181c48fbffc933a7ce9a0836f7878ca5e6d77e1-darwin-arm64.tar.gz"
|
|
4280
|
+
},
|
|
4281
|
+
"linux-x64": {
|
|
4282
|
+
sha256: "e6ac3df76c8b82f9fe2c063dd3f221324a9dad5176fd52535a5144b0ff6002d8",
|
|
4283
|
+
relativePath: "asset3d/provider/asset3d-search-provider-c181c48fbffc933a7ce9a0836f7878ca5e6d77e1-linux-x64.tar.gz"
|
|
4195
4284
|
}
|
|
4196
4285
|
});
|
|
4197
4286
|
var INSTALL_SCHEMA = "forgeax.asset3d-install/1.0.0";
|
|
@@ -4501,9 +4590,19 @@ function filesUnder2(root, directory = root) {
|
|
|
4501
4590
|
})();
|
|
4502
4591
|
});
|
|
4503
4592
|
}
|
|
4504
|
-
function
|
|
4593
|
+
function asset3dProxyLaunch(gamePluginLaunch) {
|
|
4594
|
+
if (gamePluginLaunch.args.at(-1) !== "mcp") {
|
|
4595
|
+
throw new Error("asset3d_game_plugin_launch_invalid: expected MCP launch suffix");
|
|
4596
|
+
}
|
|
4597
|
+
return {
|
|
4598
|
+
command: gamePluginLaunch.command,
|
|
4599
|
+
args: [...gamePluginLaunch.args.slice(0, -1), "asset3d", "mcp"]
|
|
4600
|
+
};
|
|
4601
|
+
}
|
|
4602
|
+
function launchEntries(projectRoot, providerCache, origins, gamePluginLaunch, catalogBaseUrl, awApiBaseUrl, awDepotName, awCredentialFile) {
|
|
4505
4603
|
const quarantine = resolve10(projectRoot, ".forgeax", "asset3d-quarantine");
|
|
4506
4604
|
const workspace = resolve10(quarantine, "workspace");
|
|
4605
|
+
const proxyLaunch = asset3dProxyLaunch(gamePluginLaunch);
|
|
4507
4606
|
return {
|
|
4508
4607
|
forgeax: {
|
|
4509
4608
|
type: "stdio",
|
|
@@ -4514,8 +4613,8 @@ function launchEntries(projectRoot, providerCache, origins, gamePluginLaunch, ca
|
|
|
4514
4613
|
},
|
|
4515
4614
|
"asset3d-search": {
|
|
4516
4615
|
type: "stdio",
|
|
4517
|
-
command:
|
|
4518
|
-
args:
|
|
4616
|
+
command: proxyLaunch.command,
|
|
4617
|
+
args: proxyLaunch.args,
|
|
4519
4618
|
env: {
|
|
4520
4619
|
FBX2GLTF_BIN: resolve10(providerCache, "bin", "FBX2glTF"),
|
|
4521
4620
|
MCP_SHARED_PATH: workspace,
|
|
@@ -4524,6 +4623,7 @@ function launchEntries(projectRoot, providerCache, origins, gamePluginLaunch, ca
|
|
|
4524
4623
|
AW_DOWNLOAD_ORIGINS: origins.compactJson,
|
|
4525
4624
|
...catalogBaseUrl ? { ASSET3D_CATALOG_BASE_URL: catalogBaseUrl } : {},
|
|
4526
4625
|
...awApiBaseUrl ? { AW_API_BASE_URL: awApiBaseUrl } : {},
|
|
4626
|
+
...awDepotName ? { AW_API_DEPOT_NAME: awDepotName } : {},
|
|
4527
4627
|
...awCredentialFile ? { AW_API_CREDENTIAL_FILE: awCredentialFile } : {}
|
|
4528
4628
|
},
|
|
4529
4629
|
toolCallTimeoutsMs: { default: 30000, tools: { search_asset: 195000 } }
|
|
@@ -4697,7 +4797,11 @@ async function publishAsset3d(options, provisioned) {
|
|
|
4697
4797
|
if (Boolean(options.awApiBaseUrl) !== Boolean(options.awCredentialFile)) {
|
|
4698
4798
|
throw new Error("asset3d_aw_config_invalid: AW API base URL and credential file must be configured together");
|
|
4699
4799
|
}
|
|
4800
|
+
if (options.awDepotName && !options.awApiBaseUrl) {
|
|
4801
|
+
throw new Error("asset3d_aw_config_invalid: depot name requires an AW API base URL");
|
|
4802
|
+
}
|
|
4700
4803
|
const awApiBaseUrl = options.awApiBaseUrl;
|
|
4804
|
+
const awDepotName = awApiBaseUrl ? options.awDepotName ?? "aw" : undefined;
|
|
4701
4805
|
const awCredentialFile = options.awCredentialFile ? resolve10(options.awCredentialFile) : undefined;
|
|
4702
4806
|
if (awCredentialFile && !isAbsolute3(options.awCredentialFile)) {
|
|
4703
4807
|
throw new Error("asset3d_credential_path_invalid: absolute path required");
|
|
@@ -4713,9 +4817,14 @@ async function publishAsset3d(options, provisioned) {
|
|
|
4713
4817
|
command: realpathSync7(options.executable ?? process.argv[1]),
|
|
4714
4818
|
args: ["mcp"]
|
|
4715
4819
|
};
|
|
4716
|
-
const entries = launchEntries(projectRoot, provisioned.cache, origins, gamePluginLaunch, catalogBaseUrl, awApiBaseUrl, awCredentialFile);
|
|
4717
|
-
if (options.verifyMcp !== false)
|
|
4718
|
-
await verifyProviderMcp(
|
|
4820
|
+
const entries = launchEntries(projectRoot, provisioned.cache, origins, gamePluginLaunch, catalogBaseUrl, awApiBaseUrl, awDepotName, awCredentialFile);
|
|
4821
|
+
if (options.verifyMcp !== false) {
|
|
4822
|
+
await verifyProviderMcp({
|
|
4823
|
+
...entries["asset3d-search"],
|
|
4824
|
+
command: resolve10(provisioned.cache, "bin", "asset3d-search"),
|
|
4825
|
+
args: []
|
|
4826
|
+
});
|
|
4827
|
+
}
|
|
4719
4828
|
const configPath = resolve10(forgeax, "mcp.json");
|
|
4720
4829
|
const merged = mergeConfig(configPath, entries, previous, options.replaceOwned === true);
|
|
4721
4830
|
const clients = [...new Set(options.clients ?? Object.keys(SKILL_MOUNTS))];
|
|
@@ -4767,6 +4876,37 @@ function prepareAsset3dProvider(options) {
|
|
|
4767
4876
|
async function installProvisionedAsset3d(options) {
|
|
4768
4877
|
return publishAsset3d(options, useProvisionedCache(options.providerCache, options.expectedSha256));
|
|
4769
4878
|
}
|
|
4879
|
+
function asset3dProviderLaunch(projectRootInput) {
|
|
4880
|
+
const projectRoot = realpathSync7(projectRootInput);
|
|
4881
|
+
const manifest = readInstallManifest(resolve10(projectRoot, ".forgeax", "asset3d-install.json"));
|
|
4882
|
+
if (!manifest)
|
|
4883
|
+
throw new Error("asset3d_not_installed");
|
|
4884
|
+
let config;
|
|
4885
|
+
try {
|
|
4886
|
+
config = JSON.parse(readFileSync12(resolve10(projectRoot, ".forgeax", "mcp.json"), "utf8"));
|
|
4887
|
+
} catch {
|
|
4888
|
+
throw new Error("project_mcp_config_invalid");
|
|
4889
|
+
}
|
|
4890
|
+
const entry = config?.mcpServers?.["asset3d-search"];
|
|
4891
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry) || entryDigest(entry) !== manifest.configEntryDigests["asset3d-search"]) {
|
|
4892
|
+
throw new Error("asset3d_provider_config_unowned");
|
|
4893
|
+
}
|
|
4894
|
+
const launch = entry;
|
|
4895
|
+
const expectedCommand = resolve10(manifest.providerCache, "bin", "asset3d-search");
|
|
4896
|
+
if (!existsSync8(expectedCommand) || !launch.env || typeof launch.env !== "object" || Array.isArray(launch.env)) {
|
|
4897
|
+
throw new Error("asset3d_provider_config_invalid");
|
|
4898
|
+
}
|
|
4899
|
+
const env3 = Object.fromEntries(Object.entries(launch.env).map(([name, value]) => {
|
|
4900
|
+
if (typeof value !== "string")
|
|
4901
|
+
throw new Error("asset3d_provider_config_invalid");
|
|
4902
|
+
return [name, value];
|
|
4903
|
+
}));
|
|
4904
|
+
return {
|
|
4905
|
+
command: expectedCommand,
|
|
4906
|
+
args: [],
|
|
4907
|
+
env: env3
|
|
4908
|
+
};
|
|
4909
|
+
}
|
|
4770
4910
|
function uninstallAsset3d(projectRootInput) {
|
|
4771
4911
|
const projectRoot = realpathSync7(projectRootInput);
|
|
4772
4912
|
const forgeax = resolve10(projectRoot, ".forgeax");
|
|
@@ -5013,6 +5153,9 @@ function forgeaxRoot(projectRoot) {
|
|
|
5013
5153
|
function journalPath(projectRoot, execution) {
|
|
5014
5154
|
return resolve11(forgeaxRoot(projectRoot), "asset3d-transactions", `${execution}.json`);
|
|
5015
5155
|
}
|
|
5156
|
+
function providerResultPath(projectRoot, execution) {
|
|
5157
|
+
return resolve11(forgeaxRoot(projectRoot), "asset3d-results", `${execution}.json`);
|
|
5158
|
+
}
|
|
5016
5159
|
function readInstall(projectRoot) {
|
|
5017
5160
|
let manifest;
|
|
5018
5161
|
try {
|
|
@@ -5072,6 +5215,36 @@ function beginAsset3d(projectRootInput, queries, options = {}) {
|
|
|
5072
5215
|
`);
|
|
5073
5216
|
return { execution, output_dir: relativeOutput };
|
|
5074
5217
|
}
|
|
5218
|
+
function asset3dSearchOutputDir(projectRootInput, execution, queries) {
|
|
5219
|
+
const projectRoot = realpathSync8(projectRootInput);
|
|
5220
|
+
const journal = readJournal(projectRoot, execution);
|
|
5221
|
+
if (journal.state !== "begun") {
|
|
5222
|
+
throw new Error(`asset3d_execution_state_invalid: ${journal.state}`);
|
|
5223
|
+
}
|
|
5224
|
+
if (queries.length !== journal.requestedCount || sha2562(canonicalJson(queries)) !== journal.queryDigest) {
|
|
5225
|
+
throw new Error("asset3d_search_query_identity_mismatch");
|
|
5226
|
+
}
|
|
5227
|
+
const outputDir = `workspace/asset3d/${execution}`;
|
|
5228
|
+
const expectedRoot = resolve11(forgeaxRoot(projectRoot), "asset3d-quarantine", outputDir);
|
|
5229
|
+
if (journal.allowedQuarantineRoot !== expectedRoot || !existsSync9(expectedRoot) || realpathSync8(expectedRoot) !== expectedRoot) {
|
|
5230
|
+
throw new Error("asset3d_search_output_identity_mismatch");
|
|
5231
|
+
}
|
|
5232
|
+
return outputDir;
|
|
5233
|
+
}
|
|
5234
|
+
function recordAsset3dProviderResult(projectRootInput, execution, providerResult) {
|
|
5235
|
+
const projectRoot = realpathSync8(projectRootInput);
|
|
5236
|
+
const journal = readJournal(projectRoot, execution);
|
|
5237
|
+
if (journal.state !== "begun") {
|
|
5238
|
+
throw new Error(`asset3d_execution_state_invalid: ${journal.state}`);
|
|
5239
|
+
}
|
|
5240
|
+
const bytes = Buffer.byteLength(providerResult);
|
|
5241
|
+
if (bytes < 1 || bytes > MAX_JSON_BYTES) {
|
|
5242
|
+
throw new Error("provider_result_too_large: expected 1 byte..1 MiB");
|
|
5243
|
+
}
|
|
5244
|
+
ensurePrivateDir(resolve11(forgeaxRoot(projectRoot), "asset3d-results"));
|
|
5245
|
+
atomicWrite(providerResultPath(projectRoot, execution), providerResult, 384);
|
|
5246
|
+
updateJournal(projectRoot, journal, { state: "provider_complete" });
|
|
5247
|
+
}
|
|
5075
5248
|
function fileBytesChecked(root, entry) {
|
|
5076
5249
|
const source = resolve11(root, entry.path);
|
|
5077
5250
|
if (!confined4(root, source))
|
|
@@ -5354,14 +5527,23 @@ function commitAsset3d(options) {
|
|
|
5354
5527
|
const projectRoot = realpathSync8(options.projectRoot);
|
|
5355
5528
|
const install = readInstall(projectRoot);
|
|
5356
5529
|
let journal = readJournal(projectRoot, options.execution);
|
|
5357
|
-
if (journal.state !== "begun" && journal.state !== "validated")
|
|
5530
|
+
if (journal.state !== "begun" && journal.state !== "provider_complete" && journal.state !== "validated") {
|
|
5358
5531
|
throw new Error(`asset3d_execution_state_invalid: ${journal.state}`);
|
|
5532
|
+
}
|
|
5359
5533
|
if (journal.providerCommit !== install.providerCommit || journal.engineCommit !== ENGINE_COMMIT || journal.engineVersion !== ENGINE_VERSION) {
|
|
5360
5534
|
throw new Error("asset3d_execution_identity_mismatch");
|
|
5361
5535
|
}
|
|
5536
|
+
let providerResult = options.providerResult;
|
|
5537
|
+
if (providerResult === undefined) {
|
|
5538
|
+
try {
|
|
5539
|
+
providerResult = readFileSync13(providerResultPath(projectRoot, options.execution));
|
|
5540
|
+
} catch {
|
|
5541
|
+
throw new Error("asset3d_provider_result_missing: call search_asset for this execution first");
|
|
5542
|
+
}
|
|
5543
|
+
}
|
|
5362
5544
|
let result;
|
|
5363
5545
|
try {
|
|
5364
|
-
result = parseProviderResult(
|
|
5546
|
+
result = parseProviderResult(providerResult, install.providerCommit, install.originSetDigest);
|
|
5365
5547
|
if (result.total !== journal.requestedCount)
|
|
5366
5548
|
throw new Error("provider_result_invalid: requested count mismatch");
|
|
5367
5549
|
const orderedQueries = [...result.results].sort((left, right) => left.queryIndex - right.queryIndex).map((item) => item.query);
|
|
@@ -5373,6 +5555,7 @@ function commitAsset3d(options) {
|
|
|
5373
5555
|
journal = updateJournal(projectRoot, journal, { state: "validated" });
|
|
5374
5556
|
} catch (error) {
|
|
5375
5557
|
rmSync6(journal.allowedQuarantineRoot, { recursive: true, force: true });
|
|
5558
|
+
rmSync6(providerResultPath(projectRoot, options.execution), { force: true });
|
|
5376
5559
|
updateJournal(projectRoot, journal, {
|
|
5377
5560
|
state: "failed",
|
|
5378
5561
|
error: error instanceof Error ? error.message.slice(0, 256) : "provider validation failed"
|
|
@@ -5392,6 +5575,7 @@ function commitAsset3d(options) {
|
|
|
5392
5575
|
}
|
|
5393
5576
|
}
|
|
5394
5577
|
rmSync6(journal.allowedQuarantineRoot, { recursive: true, force: true });
|
|
5578
|
+
rmSync6(providerResultPath(projectRoot, options.execution), { force: true });
|
|
5395
5579
|
const failed = terminal.filter((entry) => entry.status === "error").length;
|
|
5396
5580
|
updateJournal(projectRoot, journal, { state: failed === 0 ? "complete" : "failed" });
|
|
5397
5581
|
return {
|
|
@@ -5409,6 +5593,7 @@ function abortAsset3d(projectRootInput, execution) {
|
|
|
5409
5593
|
throw new Error("asset3d_execution_already_committed");
|
|
5410
5594
|
}
|
|
5411
5595
|
rmSync6(journal.allowedQuarantineRoot, { recursive: true, force: true });
|
|
5596
|
+
rmSync6(providerResultPath(projectRoot, execution), { force: true });
|
|
5412
5597
|
updateJournal(projectRoot, journal, { state: "aborted" });
|
|
5413
5598
|
return { execution, aborted: true };
|
|
5414
5599
|
}
|
|
@@ -5445,13 +5630,14 @@ import { existsSync as existsSync10 } from "node:fs";
|
|
|
5445
5630
|
import { resolve as resolve12 } from "node:path";
|
|
5446
5631
|
var AW_SERVICE_PATH = "/trpc.oasismetric.omcontentserver.http";
|
|
5447
5632
|
var ACCESS_CHECK_SCHEMA = "forgeax.asset3d-access-check/1.0.0";
|
|
5448
|
-
var
|
|
5449
|
-
|
|
5633
|
+
var DEFAULT_AW_PUBLIC_SERVICE_ROOT = "http://lb-pl74wsqg-5wi8ujmy1fq2746r.clb.usw-tencentclb.com:8008/trpc.oasismetric.omcontentserver.http";
|
|
5634
|
+
var DEFAULT_EA_LOCAL_SERVICE_ROOT = "http://test-ultrongw.woa.com/trpc.oasismetric.omcontentserver.http";
|
|
5635
|
+
function normalizeAssetLibraryServiceRoot(input) {
|
|
5450
5636
|
let parsed;
|
|
5451
5637
|
try {
|
|
5452
5638
|
parsed = new URL(input);
|
|
5453
5639
|
} catch {
|
|
5454
|
-
throw new Error("asset3d_base_url_invalid: expected an HTTP(S)
|
|
5640
|
+
throw new Error("asset3d_base_url_invalid: expected an HTTP(S) EA gateway or service URL");
|
|
5455
5641
|
}
|
|
5456
5642
|
if (!["http:", "https:"].includes(parsed.protocol) || parsed.username || parsed.password || parsed.search || parsed.hash) {
|
|
5457
5643
|
throw new Error("asset3d_base_url_invalid: credentials, query, and fragment are forbidden");
|
|
@@ -5464,11 +5650,16 @@ function normalizeAwServiceRoot(input) {
|
|
|
5464
5650
|
parsed.pathname = path;
|
|
5465
5651
|
return parsed.toString().replace(/\/$/, "");
|
|
5466
5652
|
}
|
|
5467
|
-
function
|
|
5468
|
-
const
|
|
5469
|
-
|
|
5653
|
+
function resolveAssetLibrarySelection(options = {}) {
|
|
5654
|
+
const library = options.library || process.env.FORGEAX_ASSET_LIBRARY || "aw";
|
|
5655
|
+
if (library !== "aw" && library !== "ea") {
|
|
5656
|
+
throw new Error("asset3d_library_invalid: expected aw or ea");
|
|
5657
|
+
}
|
|
5658
|
+
const defaultRoot = library === "ea" ? DEFAULT_EA_LOCAL_SERVICE_ROOT : DEFAULT_AW_PUBLIC_SERVICE_ROOT;
|
|
5659
|
+
const configured = options.baseUrl || process.env.FORGEAX_ASSET_LIBRARY_BASE_URL || defaultRoot;
|
|
5660
|
+
return { library, serviceRoot: normalizeAssetLibraryServiceRoot(configured) };
|
|
5470
5661
|
}
|
|
5471
|
-
function
|
|
5662
|
+
function checkAssetLibraryProviderAccess(options) {
|
|
5472
5663
|
const command = resolve12(options.providerCache, "bin", "asset3d-search");
|
|
5473
5664
|
if (!existsSync10(command))
|
|
5474
5665
|
throw new Error("asset3d_provider_not_prepared");
|
|
@@ -5480,6 +5671,7 @@ function checkAwProviderAccess(options) {
|
|
|
5480
5671
|
...process.env,
|
|
5481
5672
|
ASSET3D_CATALOG_BASE_URL: "",
|
|
5482
5673
|
AW_API_BASE_URL: options.serviceRoot,
|
|
5674
|
+
AW_API_DEPOT_NAME: options.depotName,
|
|
5483
5675
|
AW_API_CREDENTIAL_FILE: resolve12(options.credentialFile),
|
|
5484
5676
|
AW_API_SANDBOX_KEY: ""
|
|
5485
5677
|
}
|
|
@@ -5496,7 +5688,7 @@ function checkAwProviderAccess(options) {
|
|
|
5496
5688
|
const envelope = payload;
|
|
5497
5689
|
if (result.status !== 0 || envelope.ok !== true) {
|
|
5498
5690
|
const code = envelope.error?.code === "asset3d_access_validation_inconclusive" ? "asset3d_access_validation_inconclusive" : "asset3d_api_key_invalid_or_unavailable";
|
|
5499
|
-
throw new Error(`${code}: verify the
|
|
5691
|
+
throw new Error(`${code}: verify the selected asset-library service, network access, and Sandbox Key`);
|
|
5500
5692
|
}
|
|
5501
5693
|
if (envelope.schemaVersion !== ACCESS_CHECK_SCHEMA || envelope.value?.authentication !== "sandbox-key" || !Array.isArray(envelope.value.downloadOrigins)) {
|
|
5502
5694
|
throw new Error("asset3d_access_validation_failed: Provider returned an invalid response");
|
|
@@ -5644,6 +5836,256 @@ function writeAwCredential(pathInput, keyInput) {
|
|
|
5644
5836
|
};
|
|
5645
5837
|
}
|
|
5646
5838
|
|
|
5839
|
+
// src/asset3d/mcp-proxy.ts
|
|
5840
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
5841
|
+
async function runDormantAsset3dMcp() {
|
|
5842
|
+
let buffer = "";
|
|
5843
|
+
process.stdin.setEncoding("utf8");
|
|
5844
|
+
for await (const chunk of process.stdin) {
|
|
5845
|
+
buffer += chunk;
|
|
5846
|
+
for (;; ) {
|
|
5847
|
+
const newline = buffer.indexOf(`
|
|
5848
|
+
`);
|
|
5849
|
+
if (newline < 0)
|
|
5850
|
+
break;
|
|
5851
|
+
const line = buffer.slice(0, newline);
|
|
5852
|
+
buffer = buffer.slice(newline + 1);
|
|
5853
|
+
if (!line.trim())
|
|
5854
|
+
continue;
|
|
5855
|
+
let request2;
|
|
5856
|
+
try {
|
|
5857
|
+
request2 = JSON.parse(line);
|
|
5858
|
+
} catch {
|
|
5859
|
+
process.stdout.write(`${JSON.stringify({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error" } })}
|
|
5860
|
+
`);
|
|
5861
|
+
continue;
|
|
5862
|
+
}
|
|
5863
|
+
if (request2.id === undefined)
|
|
5864
|
+
continue;
|
|
5865
|
+
let response;
|
|
5866
|
+
if (request2.method === "initialize") {
|
|
5867
|
+
const params = request2.params;
|
|
5868
|
+
const protocolVersion = params && typeof params === "object" && !Array.isArray(params) && typeof params.protocolVersion === "string" ? params.protocolVersion : "2024-11-05";
|
|
5869
|
+
response = {
|
|
5870
|
+
jsonrpc: "2.0",
|
|
5871
|
+
id: request2.id,
|
|
5872
|
+
result: {
|
|
5873
|
+
protocolVersion,
|
|
5874
|
+
capabilities: { tools: {} },
|
|
5875
|
+
serverInfo: { name: "asset3d-search", version: "0.3.4" }
|
|
5876
|
+
}
|
|
5877
|
+
};
|
|
5878
|
+
} else if (request2.method === "tools/list") {
|
|
5879
|
+
response = { jsonrpc: "2.0", id: request2.id, result: { tools: [] } };
|
|
5880
|
+
} else {
|
|
5881
|
+
response = { jsonrpc: "2.0", id: request2.id, error: { code: -32601, message: "Method not found" } };
|
|
5882
|
+
}
|
|
5883
|
+
process.stdout.write(`${JSON.stringify(response)}
|
|
5884
|
+
`);
|
|
5885
|
+
}
|
|
5886
|
+
}
|
|
5887
|
+
return 0;
|
|
5888
|
+
}
|
|
5889
|
+
function requestKey(id) {
|
|
5890
|
+
return typeof id === "string" || typeof id === "number" ? JSON.stringify(id) : undefined;
|
|
5891
|
+
}
|
|
5892
|
+
function searchToolError(id, error) {
|
|
5893
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5894
|
+
return {
|
|
5895
|
+
jsonrpc: "2.0",
|
|
5896
|
+
id,
|
|
5897
|
+
result: {
|
|
5898
|
+
content: [{ type: "text", text: message.slice(0, 512) }],
|
|
5899
|
+
isError: true
|
|
5900
|
+
}
|
|
5901
|
+
};
|
|
5902
|
+
}
|
|
5903
|
+
function rewriteSearchTool(tool) {
|
|
5904
|
+
if (tool.name !== "search_asset")
|
|
5905
|
+
return tool;
|
|
5906
|
+
const schema = tool.inputSchema;
|
|
5907
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema))
|
|
5908
|
+
return tool;
|
|
5909
|
+
const inputSchema = schema;
|
|
5910
|
+
const properties = inputSchema.properties;
|
|
5911
|
+
if (!properties || typeof properties !== "object" || Array.isArray(properties))
|
|
5912
|
+
return tool;
|
|
5913
|
+
const { output_dir: _outputDir, ...safeProperties } = properties;
|
|
5914
|
+
const required = Array.isArray(inputSchema.required) ? inputSchema.required.filter((field) => typeof field === "string" && field !== "output_dir") : [];
|
|
5915
|
+
return {
|
|
5916
|
+
...tool,
|
|
5917
|
+
description: `${typeof tool.description === "string" ? `${tool.description} ` : ""}Run forgeax-game asset3d begin first and pass its execution ID. The bridge injects the transaction-owned output directory.`,
|
|
5918
|
+
inputSchema: {
|
|
5919
|
+
...inputSchema,
|
|
5920
|
+
properties: {
|
|
5921
|
+
...safeProperties,
|
|
5922
|
+
execution: {
|
|
5923
|
+
type: "string",
|
|
5924
|
+
pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
|
|
5925
|
+
description: "Execution ID returned by forgeax-game asset3d begin for these exact queries."
|
|
5926
|
+
}
|
|
5927
|
+
},
|
|
5928
|
+
required: [...new Set([...required, "execution"])]
|
|
5929
|
+
}
|
|
5930
|
+
};
|
|
5931
|
+
}
|
|
5932
|
+
function rewriteAsset3dToolsList(response) {
|
|
5933
|
+
const result = response.result;
|
|
5934
|
+
if (!result || typeof result !== "object" || Array.isArray(result))
|
|
5935
|
+
return response;
|
|
5936
|
+
const tools = result.tools;
|
|
5937
|
+
if (!Array.isArray(tools))
|
|
5938
|
+
return response;
|
|
5939
|
+
return {
|
|
5940
|
+
...response,
|
|
5941
|
+
result: {
|
|
5942
|
+
...result,
|
|
5943
|
+
tools: tools.map((tool) => tool && typeof tool === "object" && !Array.isArray(tool) ? rewriteSearchTool(tool) : tool)
|
|
5944
|
+
}
|
|
5945
|
+
};
|
|
5946
|
+
}
|
|
5947
|
+
function transformAsset3dSearchCall(projectRoot, request2) {
|
|
5948
|
+
if (request2.method !== "tools/call")
|
|
5949
|
+
return request2;
|
|
5950
|
+
const params = request2.params;
|
|
5951
|
+
if (!params || typeof params !== "object" || Array.isArray(params))
|
|
5952
|
+
return request2;
|
|
5953
|
+
const call = params;
|
|
5954
|
+
if (call.name !== "search_asset")
|
|
5955
|
+
return request2;
|
|
5956
|
+
const rawArguments = call.arguments;
|
|
5957
|
+
if (!rawArguments || typeof rawArguments !== "object" || Array.isArray(rawArguments)) {
|
|
5958
|
+
throw new Error("asset3d_search_arguments_invalid");
|
|
5959
|
+
}
|
|
5960
|
+
const args = rawArguments;
|
|
5961
|
+
if (typeof args.execution !== "string") {
|
|
5962
|
+
throw new Error("asset3d_execution_required: run `forgeax-game asset3d begin` first");
|
|
5963
|
+
}
|
|
5964
|
+
if (!Array.isArray(args.queries) || args.queries.some((query) => typeof query !== "string")) {
|
|
5965
|
+
throw new Error("asset3d_queries_invalid");
|
|
5966
|
+
}
|
|
5967
|
+
const outputDir = asset3dSearchOutputDir(projectRoot, args.execution, args.queries);
|
|
5968
|
+
const { execution: _execution, output_dir: _callerOutput, ...providerArguments } = args;
|
|
5969
|
+
return {
|
|
5970
|
+
...request2,
|
|
5971
|
+
params: {
|
|
5972
|
+
...call,
|
|
5973
|
+
arguments: {
|
|
5974
|
+
...providerArguments,
|
|
5975
|
+
output_dir: outputDir
|
|
5976
|
+
}
|
|
5977
|
+
}
|
|
5978
|
+
};
|
|
5979
|
+
}
|
|
5980
|
+
async function runAsset3dMcpProxy(projectRoot, launch) {
|
|
5981
|
+
const child = spawn4(launch.command, [...launch.args], {
|
|
5982
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
5983
|
+
env: { ...process.env, ...launch.env }
|
|
5984
|
+
});
|
|
5985
|
+
child.stderr.pipe(process.stderr);
|
|
5986
|
+
const toolsListRequests = new Set;
|
|
5987
|
+
const searchExecutions = new Map;
|
|
5988
|
+
let inputBuffer = "";
|
|
5989
|
+
let outputBuffer = "";
|
|
5990
|
+
process.stdin.setEncoding("utf8");
|
|
5991
|
+
process.stdin.on("data", (chunk) => {
|
|
5992
|
+
inputBuffer += chunk;
|
|
5993
|
+
for (;; ) {
|
|
5994
|
+
const newline = inputBuffer.indexOf(`
|
|
5995
|
+
`);
|
|
5996
|
+
if (newline < 0)
|
|
5997
|
+
break;
|
|
5998
|
+
const line = inputBuffer.slice(0, newline);
|
|
5999
|
+
inputBuffer = inputBuffer.slice(newline + 1);
|
|
6000
|
+
if (!line.trim())
|
|
6001
|
+
continue;
|
|
6002
|
+
let request2;
|
|
6003
|
+
try {
|
|
6004
|
+
request2 = JSON.parse(line);
|
|
6005
|
+
const key = requestKey(request2.id);
|
|
6006
|
+
if (request2.method === "tools/list" && key)
|
|
6007
|
+
toolsListRequests.add(key);
|
|
6008
|
+
const params = request2.params;
|
|
6009
|
+
if (key && request2.method === "tools/call" && params && typeof params === "object" && !Array.isArray(params) && params.name === "search_asset") {
|
|
6010
|
+
const args = params.arguments;
|
|
6011
|
+
if (args && typeof args === "object" && !Array.isArray(args) && typeof args.execution === "string") {
|
|
6012
|
+
searchExecutions.set(key, args.execution);
|
|
6013
|
+
}
|
|
6014
|
+
}
|
|
6015
|
+
request2 = transformAsset3dSearchCall(projectRoot, request2);
|
|
6016
|
+
} catch (error) {
|
|
6017
|
+
let id = null;
|
|
6018
|
+
try {
|
|
6019
|
+
id = JSON.parse(line).id ?? null;
|
|
6020
|
+
} catch {}
|
|
6021
|
+
process.stdout.write(`${JSON.stringify(searchToolError(id, error))}
|
|
6022
|
+
`);
|
|
6023
|
+
continue;
|
|
6024
|
+
}
|
|
6025
|
+
child.stdin.write(`${JSON.stringify(request2)}
|
|
6026
|
+
`);
|
|
6027
|
+
}
|
|
6028
|
+
});
|
|
6029
|
+
process.stdin.on("end", () => child.stdin.end());
|
|
6030
|
+
child.stdout.setEncoding("utf8");
|
|
6031
|
+
child.stdout.on("data", (chunk) => {
|
|
6032
|
+
outputBuffer += chunk;
|
|
6033
|
+
for (;; ) {
|
|
6034
|
+
const newline = outputBuffer.indexOf(`
|
|
6035
|
+
`);
|
|
6036
|
+
if (newline < 0)
|
|
6037
|
+
break;
|
|
6038
|
+
const line = outputBuffer.slice(0, newline);
|
|
6039
|
+
outputBuffer = outputBuffer.slice(newline + 1);
|
|
6040
|
+
if (!line.trim())
|
|
6041
|
+
continue;
|
|
6042
|
+
try {
|
|
6043
|
+
let response = JSON.parse(line);
|
|
6044
|
+
const key = requestKey(response.id);
|
|
6045
|
+
if (key && toolsListRequests.delete(key))
|
|
6046
|
+
response = rewriteAsset3dToolsList(response);
|
|
6047
|
+
const execution = key ? searchExecutions.get(key) : undefined;
|
|
6048
|
+
if (key && execution) {
|
|
6049
|
+
searchExecutions.delete(key);
|
|
6050
|
+
try {
|
|
6051
|
+
const result = response.result;
|
|
6052
|
+
if (!result || typeof result !== "object" || Array.isArray(result) || result.isError === true) {
|
|
6053
|
+
throw new Error("asset3d_provider_result_missing");
|
|
6054
|
+
}
|
|
6055
|
+
const content = result.content;
|
|
6056
|
+
if (!Array.isArray(content) || content.length !== 1) {
|
|
6057
|
+
throw new Error("asset3d_provider_result_invalid");
|
|
6058
|
+
}
|
|
6059
|
+
const text2 = content[0];
|
|
6060
|
+
if (!text2 || typeof text2 !== "object" || Array.isArray(text2) || text2.type !== "text" || typeof text2.text !== "string") {
|
|
6061
|
+
throw new Error("asset3d_provider_result_invalid");
|
|
6062
|
+
}
|
|
6063
|
+
recordAsset3dProviderResult(projectRoot, execution, text2.text);
|
|
6064
|
+
} catch (error) {
|
|
6065
|
+
response = searchToolError(response.id, error);
|
|
6066
|
+
}
|
|
6067
|
+
}
|
|
6068
|
+
process.stdout.write(`${JSON.stringify(response)}
|
|
6069
|
+
`);
|
|
6070
|
+
} catch {
|
|
6071
|
+
process.stderr.write(`asset3d_provider_mcp_stdout_invalid
|
|
6072
|
+
`);
|
|
6073
|
+
child.kill("SIGTERM");
|
|
6074
|
+
}
|
|
6075
|
+
}
|
|
6076
|
+
});
|
|
6077
|
+
const stopChild = () => {
|
|
6078
|
+
if (child.exitCode === null && child.signalCode === null)
|
|
6079
|
+
child.kill("SIGTERM");
|
|
6080
|
+
};
|
|
6081
|
+
process.once("SIGINT", stopChild);
|
|
6082
|
+
process.once("SIGTERM", stopChild);
|
|
6083
|
+
return await new Promise((resolve14, reject) => {
|
|
6084
|
+
child.once("error", reject);
|
|
6085
|
+
child.once("close", (code, signal) => resolve14(code ?? (signal ? 1 : 0)));
|
|
6086
|
+
});
|
|
6087
|
+
}
|
|
6088
|
+
|
|
5647
6089
|
// src/cli/dispatch.ts
|
|
5648
6090
|
var HELP = `ForgeaX game development plugin
|
|
5649
6091
|
|
|
@@ -5656,14 +6098,15 @@ Usage:
|
|
|
5656
6098
|
forgeax-game preview stop [--game <slug>] [--target-dir <path>] [--json]
|
|
5657
6099
|
forgeax-game devkit install
|
|
5658
6100
|
forgeax-game agents update
|
|
5659
|
-
forgeax-game asset3d enable [--base-url <
|
|
6101
|
+
forgeax-game asset3d enable [--library aw|ea] [--base-url <gateway/service URL>] [--ide ${CLIENT_CHOICES.join(",")}]
|
|
5660
6102
|
forgeax-game asset3d install --provider-bundle <archive> --sha256 <hex> --download-origin <scheme://host:port> [--aw-base-url <URL> --aw-credential-file <absolute path>] [...]
|
|
5661
6103
|
forgeax-game asset3d uninstall
|
|
5662
6104
|
forgeax-game asset3d begin --query <text> [--query <text> ...] --json
|
|
5663
|
-
forgeax-game asset3d commit --execution <uuid> --provider-result-stdin
|
|
6105
|
+
forgeax-game asset3d commit --execution <uuid> --json [--provider-result-stdin] [--refresh]
|
|
5664
6106
|
forgeax-game asset3d abort --execution <uuid> --json
|
|
5665
6107
|
forgeax-game asset3d doctor --json
|
|
5666
6108
|
forgeax-game update [--ide ...]
|
|
6109
|
+
forgeax-game version
|
|
5667
6110
|
forgeax-game help
|
|
5668
6111
|
|
|
5669
6112
|
With no arguments, forgeax-game runs the stdio MCP server.
|
|
@@ -6090,6 +6533,16 @@ async function doctorCommand(args) {
|
|
|
6090
6533
|
return warnings === 0 ? 0 : 1;
|
|
6091
6534
|
}
|
|
6092
6535
|
var UPDATE_USAGE = "usage: forgeax-game update [--ide codex,claude,cursor,...]";
|
|
6536
|
+
function versionCommand(args) {
|
|
6537
|
+
if (args.length > 0)
|
|
6538
|
+
throw new Error("usage: forgeax-game version");
|
|
6539
|
+
process.stdout.write(`${RELEASE_IDENTITY.gamePackage} ${RELEASE_IDENTITY.gameVersion}
|
|
6540
|
+
`);
|
|
6541
|
+
return 0;
|
|
6542
|
+
}
|
|
6543
|
+
function formatVersionTransition(previousVersion, currentVersion = RELEASE_IDENTITY.gameVersion) {
|
|
6544
|
+
return previousVersion === currentVersion ? currentVersion : `${previousVersion ?? "unknown"} -> ${currentVersion}`;
|
|
6545
|
+
}
|
|
6093
6546
|
async function updateCommand(args) {
|
|
6094
6547
|
const requested = parseIdeSelector(args, UPDATE_USAGE);
|
|
6095
6548
|
const project = resolveProject();
|
|
@@ -6114,8 +6567,9 @@ async function updateCommand(args) {
|
|
|
6114
6567
|
`);
|
|
6115
6568
|
await verifyLaunch(launch);
|
|
6116
6569
|
for (const client of configured) {
|
|
6570
|
+
const previousVersion = configuredGameVersion(client, root);
|
|
6117
6571
|
const result = applyConfig(client, root, launch);
|
|
6118
|
-
process.stdout.write(`${result.changed ? "UPDATED" : "CURRENT"} ${client.label}: ${result.path}
|
|
6572
|
+
process.stdout.write(`${result.changed ? "UPDATED" : "CURRENT"} ${client.label}: ${result.path} (plugin ${formatVersionTransition(previousVersion)})
|
|
6119
6573
|
`);
|
|
6120
6574
|
}
|
|
6121
6575
|
if (project.root) {
|
|
@@ -6156,15 +6610,32 @@ function canonicalAsset3dClients(value) {
|
|
|
6156
6610
|
throw new Error(`asset3d_client_invalid: ${invalid.join(", ")}`);
|
|
6157
6611
|
return [...new Set(values)];
|
|
6158
6612
|
}
|
|
6613
|
+
function parseAssetLibraryId(value) {
|
|
6614
|
+
if (value === "aw" || value === "ea")
|
|
6615
|
+
return value;
|
|
6616
|
+
throw new Error("asset3d_library_invalid: expected aw or ea");
|
|
6617
|
+
}
|
|
6159
6618
|
function parseAsset3dEnableArgs(args) {
|
|
6160
6619
|
const ideArgs = [];
|
|
6161
6620
|
let baseUrl;
|
|
6621
|
+
let library;
|
|
6162
6622
|
for (let index = 0;index < args.length; index++) {
|
|
6163
6623
|
const arg = args[index];
|
|
6624
|
+
if (arg === "--library") {
|
|
6625
|
+
const value = args[++index];
|
|
6626
|
+
if (!value)
|
|
6627
|
+
throw new Error("--library requires aw or ea");
|
|
6628
|
+
library = parseAssetLibraryId(value);
|
|
6629
|
+
continue;
|
|
6630
|
+
}
|
|
6631
|
+
if (arg.startsWith("--library=")) {
|
|
6632
|
+
library = parseAssetLibraryId(arg.slice("--library=".length));
|
|
6633
|
+
continue;
|
|
6634
|
+
}
|
|
6164
6635
|
if (arg === "--base-url") {
|
|
6165
6636
|
const value = args[++index];
|
|
6166
6637
|
if (!value)
|
|
6167
|
-
throw new Error("--base-url requires
|
|
6638
|
+
throw new Error("--base-url requires a gateway/service URL");
|
|
6168
6639
|
baseUrl = value;
|
|
6169
6640
|
continue;
|
|
6170
6641
|
}
|
|
@@ -6183,14 +6654,31 @@ function parseAsset3dEnableArgs(args) {
|
|
|
6183
6654
|
ideArgs.push(arg);
|
|
6184
6655
|
continue;
|
|
6185
6656
|
}
|
|
6186
|
-
throw new Error("usage: forgeax-game asset3d enable [--base-url <
|
|
6657
|
+
throw new Error("usage: forgeax-game asset3d enable [--library aw|ea] [--base-url <gateway/service URL>] [--ide codex,claude,cursor,...]");
|
|
6187
6658
|
}
|
|
6188
|
-
return { requested: parseIdeSelector(ideArgs, "invalid --ide selector"), baseUrl };
|
|
6659
|
+
return { requested: parseIdeSelector(ideArgs, "invalid --ide selector"), baseUrl, library };
|
|
6189
6660
|
}
|
|
6190
6661
|
async function asset3dCommand(args) {
|
|
6191
6662
|
const [operation, ...rest] = args;
|
|
6192
|
-
const projectRoot = requireProject();
|
|
6193
6663
|
try {
|
|
6664
|
+
if (operation === "mcp") {
|
|
6665
|
+
if (rest.length)
|
|
6666
|
+
throw new Error("usage: forgeax-game asset3d mcp");
|
|
6667
|
+
const binding = resolveProject();
|
|
6668
|
+
if (!binding.root)
|
|
6669
|
+
return runDormantAsset3dMcp();
|
|
6670
|
+
let launch;
|
|
6671
|
+
try {
|
|
6672
|
+
launch = asset3dProviderLaunch(binding.root);
|
|
6673
|
+
} catch (error) {
|
|
6674
|
+
if (error instanceof Error && error.message === "asset3d_not_installed") {
|
|
6675
|
+
return runDormantAsset3dMcp();
|
|
6676
|
+
}
|
|
6677
|
+
throw error;
|
|
6678
|
+
}
|
|
6679
|
+
return runAsset3dMcpProxy(binding.root, launch);
|
|
6680
|
+
}
|
|
6681
|
+
const projectRoot = requireProject();
|
|
6194
6682
|
if (operation === "enable") {
|
|
6195
6683
|
const parsed = parseAsset3dEnableArgs(rest);
|
|
6196
6684
|
const selection = selectClients(projectRoot, parsed.requested);
|
|
@@ -6198,7 +6686,23 @@ async function asset3dCommand(args) {
|
|
|
6198
6686
|
if (selection.selected.length === 0) {
|
|
6199
6687
|
throw new Error("asset3d_client_missing: run `forgeax-game install --ide <client>` before enabling Asset3D");
|
|
6200
6688
|
}
|
|
6201
|
-
const
|
|
6689
|
+
const bridge = asset3dLaunchSpec("npx");
|
|
6690
|
+
for (const id of selection.selected) {
|
|
6691
|
+
const client = findClient(id);
|
|
6692
|
+
if (!client)
|
|
6693
|
+
throw new Error(`asset3d_client_invalid: ${id}`);
|
|
6694
|
+
const state = inspectConfig(client, projectRoot, bridge, ASSET3D_SERVER_KEY);
|
|
6695
|
+
if (state.state === "different") {
|
|
6696
|
+
throw new Error(`asset3d_client_server_conflict: ${client.label} already has a different ${ASSET3D_SERVER_KEY} entry`);
|
|
6697
|
+
}
|
|
6698
|
+
if (state.state === "invalid") {
|
|
6699
|
+
throw new Error(`asset3d_client_config_invalid: ${client.label}: ${state.detail ?? state.path}`);
|
|
6700
|
+
}
|
|
6701
|
+
}
|
|
6702
|
+
const library = resolveAssetLibrarySelection({
|
|
6703
|
+
library: parsed.library,
|
|
6704
|
+
baseUrl: parsed.baseUrl
|
|
6705
|
+
});
|
|
6202
6706
|
const os = platform2() === "win32" ? "windows" : platform2();
|
|
6203
6707
|
const cpu = arch2();
|
|
6204
6708
|
const target = `${os}-${cpu}`;
|
|
@@ -6216,20 +6720,37 @@ async function asset3dCommand(args) {
|
|
|
6216
6720
|
const credentialWrite = writeAwCredential(credentialFile, credential.key);
|
|
6217
6721
|
const clients = selection.selected.filter((id) => (id in SKILL_MOUNTS));
|
|
6218
6722
|
try {
|
|
6219
|
-
const access =
|
|
6723
|
+
const access = checkAssetLibraryProviderAccess({
|
|
6724
|
+
providerCache: preparedCache,
|
|
6725
|
+
depotName: library.library,
|
|
6726
|
+
serviceRoot: library.serviceRoot,
|
|
6727
|
+
credentialFile
|
|
6728
|
+
});
|
|
6220
6729
|
const result = await installProvisionedAsset3d({
|
|
6221
6730
|
projectRoot,
|
|
6222
6731
|
providerCache: preparedCache,
|
|
6223
6732
|
expectedSha256: bundled.sha256,
|
|
6224
6733
|
downloadOrigins: access.downloadOrigins,
|
|
6225
6734
|
awApiBaseUrl: access.serviceRoot,
|
|
6735
|
+
awDepotName: library.library,
|
|
6226
6736
|
awCredentialFile: credentialFile,
|
|
6227
6737
|
clients,
|
|
6228
6738
|
gamePluginLaunch: launchSpec("npx"),
|
|
6229
6739
|
replaceOwned: true
|
|
6230
6740
|
});
|
|
6741
|
+
for (const id of selection.selected) {
|
|
6742
|
+
const client = findClient(id);
|
|
6743
|
+
if (!client)
|
|
6744
|
+
throw new Error(`asset3d_client_invalid: ${id}`);
|
|
6745
|
+
const applied = applyConfig(client, projectRoot, bridge, ASSET3D_SERVER_KEY);
|
|
6746
|
+
process.stdout.write(`${applied.changed ? "UPDATED" : "CURRENT"} ${client.label} Asset3D bridge: ${applied.path}
|
|
6747
|
+
`);
|
|
6748
|
+
if (applied.changed && client.postInstallNote)
|
|
6749
|
+
process.stdout.write(` ${client.postInstallNote}
|
|
6750
|
+
`);
|
|
6751
|
+
}
|
|
6231
6752
|
credentialWrite.commit();
|
|
6232
|
-
process.stdout.write(`${result.changed ? "ENABLED" : "CURRENT"} Asset3D provider ${result.providerCommit}; service=${access.serviceRoot}; authentication=${access.authentication}; skillFiles=${result.skillFiles}
|
|
6753
|
+
process.stdout.write(`${result.changed ? "ENABLED" : "CURRENT"} Asset3D provider ${result.providerCommit}; library=${library.library}; service=${access.serviceRoot}; authentication=${access.authentication}; skillFiles=${result.skillFiles}
|
|
6233
6754
|
`);
|
|
6234
6755
|
return 0;
|
|
6235
6756
|
} catch (error) {
|
|
@@ -6242,6 +6763,7 @@ async function asset3dCommand(args) {
|
|
|
6242
6763
|
let expectedSha256;
|
|
6243
6764
|
let catalogBaseUrl;
|
|
6244
6765
|
let awApiBaseUrl;
|
|
6766
|
+
let awDepotName;
|
|
6245
6767
|
let awCredentialFile;
|
|
6246
6768
|
const downloadOrigins = [];
|
|
6247
6769
|
let clients;
|
|
@@ -6252,7 +6774,7 @@ async function asset3dCommand(args) {
|
|
|
6252
6774
|
replaceOwned = true;
|
|
6253
6775
|
continue;
|
|
6254
6776
|
}
|
|
6255
|
-
if (arg === "--provider-bundle" || arg === "--sha256" || arg === "--download-origin" || arg === "--catalog-base-url" || arg === "--aw-base-url" || arg === "--aw-credential-file" || arg === "--ide") {
|
|
6777
|
+
if (arg === "--provider-bundle" || arg === "--sha256" || arg === "--download-origin" || arg === "--catalog-base-url" || arg === "--aw-base-url" || arg === "--aw-depot-name" || arg === "--aw-credential-file" || arg === "--ide") {
|
|
6256
6778
|
const value = rest[++index];
|
|
6257
6779
|
if (!value)
|
|
6258
6780
|
throw new Error(`${arg} requires a value`);
|
|
@@ -6265,7 +6787,9 @@ async function asset3dCommand(args) {
|
|
|
6265
6787
|
else if (arg === "--catalog-base-url")
|
|
6266
6788
|
catalogBaseUrl = value;
|
|
6267
6789
|
else if (arg === "--aw-base-url")
|
|
6268
|
-
awApiBaseUrl =
|
|
6790
|
+
awApiBaseUrl = normalizeAssetLibraryServiceRoot(value);
|
|
6791
|
+
else if (arg === "--aw-depot-name")
|
|
6792
|
+
awDepotName = parseAssetLibraryId(value);
|
|
6269
6793
|
else if (arg === "--aw-credential-file")
|
|
6270
6794
|
awCredentialFile = resolve14(value);
|
|
6271
6795
|
else
|
|
@@ -6283,6 +6807,7 @@ async function asset3dCommand(args) {
|
|
|
6283
6807
|
downloadOrigins,
|
|
6284
6808
|
...catalogBaseUrl ? { catalogBaseUrl } : {},
|
|
6285
6809
|
...awApiBaseUrl ? { awApiBaseUrl } : {},
|
|
6810
|
+
...awDepotName ? { awDepotName } : {},
|
|
6286
6811
|
...awCredentialFile ? { awCredentialFile } : {},
|
|
6287
6812
|
...clients ? { clients } : {},
|
|
6288
6813
|
replaceOwned
|
|
@@ -6333,11 +6858,16 @@ async function asset3dCommand(args) {
|
|
|
6333
6858
|
execution = rest[++index];
|
|
6334
6859
|
continue;
|
|
6335
6860
|
}
|
|
6336
|
-
throw new Error("usage: forgeax-game asset3d commit --execution <uuid> --provider-result-stdin
|
|
6861
|
+
throw new Error("usage: forgeax-game asset3d commit --execution <uuid> --json [--provider-result-stdin] [--refresh]");
|
|
6337
6862
|
}
|
|
6338
|
-
if (!execution
|
|
6339
|
-
throw new Error("usage: forgeax-game asset3d commit --execution <uuid> --provider-result-stdin
|
|
6340
|
-
const result = commitAsset3d({
|
|
6863
|
+
if (!execution)
|
|
6864
|
+
throw new Error("usage: forgeax-game asset3d commit --execution <uuid> --json [--provider-result-stdin] [--refresh]");
|
|
6865
|
+
const result = commitAsset3d({
|
|
6866
|
+
projectRoot,
|
|
6867
|
+
execution,
|
|
6868
|
+
...stdin ? { providerResult: await readBoundedStdin() } : {},
|
|
6869
|
+
refresh
|
|
6870
|
+
});
|
|
6341
6871
|
asset3dEnvelope("asset3d.commit", result.failed === 0, result);
|
|
6342
6872
|
return result.failed === 0 ? 0 : 1;
|
|
6343
6873
|
}
|
|
@@ -6388,6 +6918,10 @@ async function runCli(argv) {
|
|
|
6388
6918
|
return asset3dCommand(args);
|
|
6389
6919
|
case "update":
|
|
6390
6920
|
return updateCommand(args);
|
|
6921
|
+
case "version":
|
|
6922
|
+
case "--version":
|
|
6923
|
+
case "-v":
|
|
6924
|
+
return versionCommand(args);
|
|
6391
6925
|
case "help":
|
|
6392
6926
|
case "--help":
|
|
6393
6927
|
case "-h":
|