castle-cli-reboot 1.0.3
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/gd.js +135 -0
- package/package.json +13 -0
- package/tool/castle-terminal.js +72 -0
- package/tool/gd.js +115 -0
- package/tool/sd.js +175 -0
package/gd.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import readline from 'readline';
|
|
5
|
+
|
|
6
|
+
function getCastleModulePath(subpath) {
|
|
7
|
+
const localPath = path.resolve('./node_modules/castle-cli/dist', subpath);
|
|
8
|
+
if (fs.existsSync(localPath)) return localPath;
|
|
9
|
+
const parentLocalPath = path.resolve('../node_modules/castle-cli/dist', subpath);
|
|
10
|
+
if (fs.existsSync(parentLocalPath)) return parentLocalPath;
|
|
11
|
+
try {
|
|
12
|
+
const npmRoot = execSync('npm root -g', { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim();
|
|
13
|
+
const globalPath = path.join(npmRoot, 'castle-cli', 'dist', subpath);
|
|
14
|
+
if (fs.existsSync(globalPath)) return globalPath;
|
|
15
|
+
} catch (e) {}
|
|
16
|
+
throw new Error(`Could not find castle-cli module '${subpath}'. Make sure castle-cli is installed locally or globally.`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const API = await import(`file://${getCastleModulePath('api.js')}`);
|
|
20
|
+
const { setToken, getToken } = await import(`file://${getCastleModulePath('config.js')}`);
|
|
21
|
+
|
|
22
|
+
function promptInput(query) {
|
|
23
|
+
const rl = readline.createInterface({
|
|
24
|
+
input: process.stdin,
|
|
25
|
+
output: process.stdout,
|
|
26
|
+
});
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
rl.question(query, (answer) => {
|
|
29
|
+
rl.close();
|
|
30
|
+
resolve(answer.trim());
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function ensureAuth() {
|
|
36
|
+
const savedToken = getToken();
|
|
37
|
+
const promptMsg = savedToken
|
|
38
|
+
? 'Enter Castle Auth Token (or press Enter to use saved token): '
|
|
39
|
+
: 'Enter Castle Auth Token: ';
|
|
40
|
+
|
|
41
|
+
const inputToken = await promptInput(promptMsg);
|
|
42
|
+
if (inputToken) {
|
|
43
|
+
setToken(inputToken);
|
|
44
|
+
} else if (!savedToken) {
|
|
45
|
+
console.error("Error: Auth token is required.");
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function getDeck(deckId, customPath) {
|
|
52
|
+
if (!deckId) {
|
|
53
|
+
console.error("Error: gd need's atleast two info, the deckId, then the Folder. like this: gd <deckId> <Folder>");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!(await ensureAuth())) return;
|
|
58
|
+
|
|
59
|
+
const deckDir = path.resolve(customPath || '/sdcard/deck');
|
|
60
|
+
console.log(`Getting ${deckId} to ${deckDir}...`);
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const deck = await API.deck(deckId);
|
|
64
|
+
if (!deck) {
|
|
65
|
+
console.error("You're not allowed to get this deck or it doesn't exist..");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
fs.rmSync(deckDir, { recursive: true, force: true });
|
|
70
|
+
fs.mkdirSync(deckDir, { recursive: true });
|
|
71
|
+
|
|
72
|
+
const cards = deck.cards?.length ? deck.cards : deck.initialCard ? [deck.initialCard] : [];
|
|
73
|
+
|
|
74
|
+
fs.writeFileSync(
|
|
75
|
+
path.join(deckDir, 'deck.json'),
|
|
76
|
+
JSON.stringify({
|
|
77
|
+
deckId: deck.deckId,
|
|
78
|
+
title: deck.title ?? '',
|
|
79
|
+
visibility: deck.visibility,
|
|
80
|
+
variables: deck.variables ?? [],
|
|
81
|
+
initialCard: deck.initialCard,
|
|
82
|
+
cards,
|
|
83
|
+
}, null, 2)
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
for (const card of cards) {
|
|
87
|
+
if (!card.cardId || !card.sceneDataUrl) continue;
|
|
88
|
+
console.log(`Decompiling Scene.json for card ${card.cardId}...`);
|
|
89
|
+
|
|
90
|
+
const sceneData = await API.downloadSceneData(card.sceneDataUrl);
|
|
91
|
+
const cardDir = path.join(deckDir, 'cards', card.cardId);
|
|
92
|
+
fs.mkdirSync(cardDir, { recursive: true });
|
|
93
|
+
|
|
94
|
+
fs.writeFileSync(path.join(cardDir, 'card.json'), JSON.stringify(card, null, 2));
|
|
95
|
+
|
|
96
|
+
const sceneDir = path.join(cardDir, 'scene');
|
|
97
|
+
fs.mkdirSync(sceneDir, { recursive: true });
|
|
98
|
+
|
|
99
|
+
const sceneObj = typeof sceneData === 'string' ? JSON.parse(sceneData) : sceneData;
|
|
100
|
+
fs.writeFileSync(path.join(sceneDir, 'scene.json'), JSON.stringify(sceneObj, null, 2));
|
|
101
|
+
|
|
102
|
+
const library = sceneObj.snapshot?.library || {};
|
|
103
|
+
const actors = sceneObj.snapshot?.actors || [];
|
|
104
|
+
const blueprintsDir = path.join(sceneDir, 'blueprints');
|
|
105
|
+
fs.mkdirSync(blueprintsDir, { recursive: true });
|
|
106
|
+
|
|
107
|
+
const slugMap = {};
|
|
108
|
+
for (const [uuid, entry] of Object.entries(library)) {
|
|
109
|
+
if (entry.entryType === 'actorBlueprint') {
|
|
110
|
+
const safeName = (entry.title || uuid).replace(/[^a-zA-Z0-9_-]/g, '-').toLowerCase();
|
|
111
|
+
slugMap[safeName] = uuid;
|
|
112
|
+
fs.writeFileSync(path.join(blueprintsDir, `${safeName}.json`), JSON.stringify(entry, null, 2));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let actorsYaml = "";
|
|
117
|
+
actors.forEach((actor, index) => {
|
|
118
|
+
actorsYaml += `a${index}:\n`;
|
|
119
|
+
if (actor.title) actorsYaml += ` title: "${actor.title}"\n`;
|
|
120
|
+
if (actor.blueprintId) actorsYaml += ` blueprint: "${actor.blueprintId}"\n`;
|
|
121
|
+
actorsYaml += ` x: ${actor.x || 0}\n`;
|
|
122
|
+
actorsYaml += ` y: ${actor.y || 0}\n`;
|
|
123
|
+
});
|
|
124
|
+
fs.writeFileSync(path.join(sceneDir, 'actors.yaml'), actorsYaml);
|
|
125
|
+
|
|
126
|
+
const castleDir = path.join(sceneDir, '.castle');
|
|
127
|
+
fs.mkdirSync(castleDir, { recursive: true });
|
|
128
|
+
fs.writeFileSync(path.join(castleDir, 'slug-map.json'), JSON.stringify(slugMap, null, 2));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.log(`Decompiled deck in ${deckDir}`);
|
|
132
|
+
} catch (err) {
|
|
133
|
+
console.error("Error during the decompilation", err);
|
|
134
|
+
}
|
|
135
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import readline from 'readline';
|
|
3
|
+
import { getDeck } from './gd.js';
|
|
4
|
+
import { saveDeck } from './sd.js';
|
|
5
|
+
|
|
6
|
+
const rl = readline.createInterface({
|
|
7
|
+
input: process.stdin,
|
|
8
|
+
output: process.stdout,
|
|
9
|
+
prompt: 'castle-cli> '
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
console.clear();
|
|
13
|
+
console.log("======================================================");
|
|
14
|
+
console.log(" Castle Custom Terminal ");
|
|
15
|
+
console.log("Available Commands: ");
|
|
16
|
+
console.log(" gd <id> [folder] - Extracts deck using its ID ");
|
|
17
|
+
console.log(" sd [folder] - Saves the local deck ");
|
|
18
|
+
console.log(" exit - Exits Castle custom terminal ");
|
|
19
|
+
console.log("======================================================");
|
|
20
|
+
|
|
21
|
+
rl.prompt();
|
|
22
|
+
|
|
23
|
+
rl.on('line', async (line) => {
|
|
24
|
+
let input = line.trim();
|
|
25
|
+
|
|
26
|
+
if (!input) {
|
|
27
|
+
rl.prompt();
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Handle space-separated command aliases like "get deck" or "save deck"
|
|
32
|
+
if (/^get\s+deck\b/i.test(input)) {
|
|
33
|
+
input = input.replace(/^get\s+deck\b/i, 'gd');
|
|
34
|
+
} else if (/^save\s+deck\b/i.test(input)) {
|
|
35
|
+
input = input.replace(/^save\s+deck\b/i, 'sd');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const parts = input.split(/\s+/);
|
|
39
|
+
const cmd = parts[0].toLowerCase();
|
|
40
|
+
|
|
41
|
+
switch (cmd) {
|
|
42
|
+
case 'gd':
|
|
43
|
+
case 'get-deck':
|
|
44
|
+
case 'getdeck':
|
|
45
|
+
case 'get': {
|
|
46
|
+
const deckId = parts[1];
|
|
47
|
+
const customPath = parts[2];
|
|
48
|
+
await getDeck(deckId, customPath);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
case 'sd':
|
|
52
|
+
case 'save-deck':
|
|
53
|
+
case 'savedeck':
|
|
54
|
+
case 'save': {
|
|
55
|
+
const customPath = parts[1];
|
|
56
|
+
await saveDeck(customPath);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
case 'exit':
|
|
60
|
+
case 'quit':
|
|
61
|
+
console.log("Exiting Terminal...");
|
|
62
|
+
process.exit(0);
|
|
63
|
+
break;
|
|
64
|
+
default:
|
|
65
|
+
console.log(`Unknown Command: "${parts[0]}"`);
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
rl.prompt();
|
|
69
|
+
}).on('close', () => {
|
|
70
|
+
console.log('\nClosing Terminal.');
|
|
71
|
+
process.exit(0);
|
|
72
|
+
});
|
package/tool/gd.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import readline from 'readline';
|
|
5
|
+
|
|
6
|
+
const npmRoot = execSync('npm root -g').toString().trim();
|
|
7
|
+
const apiPath = path.join(npmRoot, 'castle-cli', 'dist', 'api.js');
|
|
8
|
+
const configPath = path.join(npmRoot, 'castle-cli', 'dist', 'config.js');
|
|
9
|
+
|
|
10
|
+
const API = await import(`file://${apiPath}`);
|
|
11
|
+
const { setToken } = await import(`file://${configPath}`);
|
|
12
|
+
|
|
13
|
+
function promptToken() {
|
|
14
|
+
const rl = readline.createInterface({
|
|
15
|
+
input: process.stdin,
|
|
16
|
+
output: process.stdout,
|
|
17
|
+
});
|
|
18
|
+
return new Promise((resolve) => {
|
|
19
|
+
rl.question('Enter Castle Auth Token: ', (answer) => {
|
|
20
|
+
rl.close();
|
|
21
|
+
resolve(answer.trim());
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function getDeck(deckId, customPath) {
|
|
27
|
+
if (!deckId) {
|
|
28
|
+
console.error("Error: gd need's atleast two info, the deckId, then the Folder. like this: gd <deckId> <Folder>");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const token = await promptToken();
|
|
33
|
+
if (!token) {
|
|
34
|
+
console.error("Error: Auth token is required.");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
setToken(token);
|
|
38
|
+
|
|
39
|
+
const deckDir = path.resolve(customPath || '/sdcard/deck');
|
|
40
|
+
console.log(`Getting ${deckId} to ${deckDir}...`);
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const deck = await API.deck(deckId);
|
|
44
|
+
if (!deck) {
|
|
45
|
+
console.error("You're not allowed to get this deck or it doesn't exist..");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fs.rmSync(deckDir, { recursive: true, force: true });
|
|
50
|
+
fs.mkdirSync(deckDir, { recursive: true });
|
|
51
|
+
|
|
52
|
+
const cards = deck.cards?.length ? deck.cards : deck.initialCard ? [deck.initialCard] : [];
|
|
53
|
+
|
|
54
|
+
fs.writeFileSync(
|
|
55
|
+
path.join(deckDir, 'deck.json'),
|
|
56
|
+
JSON.stringify({
|
|
57
|
+
deckId: deck.deckId,
|
|
58
|
+
title: deck.title ?? '',
|
|
59
|
+
visibility: deck.visibility,
|
|
60
|
+
variables: deck.variables ?? [],
|
|
61
|
+
initialCard: deck.initialCard,
|
|
62
|
+
cards,
|
|
63
|
+
}, null, 2)
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
for (const card of cards) {
|
|
67
|
+
if (!card.cardId || !card.sceneDataUrl) continue;
|
|
68
|
+
console.log(`Decompiling Scene.json for card ${card.cardId}...`);
|
|
69
|
+
|
|
70
|
+
const sceneData = await API.downloadSceneData(card.sceneDataUrl);
|
|
71
|
+
const cardDir = path.join(deckDir, 'cards', card.cardId);
|
|
72
|
+
fs.mkdirSync(cardDir, { recursive: true });
|
|
73
|
+
|
|
74
|
+
fs.writeFileSync(path.join(cardDir, 'card.json'), JSON.stringify(card, null, 2));
|
|
75
|
+
|
|
76
|
+
const sceneDir = path.join(cardDir, 'scene');
|
|
77
|
+
fs.mkdirSync(sceneDir, { recursive: true });
|
|
78
|
+
|
|
79
|
+
const sceneObj = typeof sceneData === 'string' ? JSON.parse(sceneData) : sceneData;
|
|
80
|
+
fs.writeFileSync(path.join(sceneDir, 'scene.json'), JSON.stringify(sceneObj, null, 2));
|
|
81
|
+
|
|
82
|
+
const library = sceneObj.snapshot?.library || {};
|
|
83
|
+
const actors = sceneObj.snapshot?.actors || [];
|
|
84
|
+
const blueprintsDir = path.join(sceneDir, 'blueprints');
|
|
85
|
+
fs.mkdirSync(blueprintsDir, { recursive: true });
|
|
86
|
+
|
|
87
|
+
const slugMap = {};
|
|
88
|
+
for (const [uuid, entry] of Object.entries(library)) {
|
|
89
|
+
if (entry.entryType === 'actorBlueprint') {
|
|
90
|
+
const safeName = (entry.title || uuid).replace(/[^a-zA-Z0-9_-]/g, '-').toLowerCase();
|
|
91
|
+
slugMap[safeName] = uuid;
|
|
92
|
+
fs.writeFileSync(path.join(blueprintsDir, `${safeName}.json`), JSON.stringify(entry, null, 2));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let actorsYaml = "";
|
|
97
|
+
actors.forEach((actor, index) => {
|
|
98
|
+
actorsYaml += `a${index}:\n`;
|
|
99
|
+
if (actor.title) actorsYaml += ` title: "${actor.title}"\n`;
|
|
100
|
+
if (actor.blueprintId) actorsYaml += ` blueprint: "${actor.blueprintId}"\n`;
|
|
101
|
+
actorsYaml += ` x: ${actor.x || 0}\n`;
|
|
102
|
+
actorsYaml += ` y: ${actor.y || 0}\n`;
|
|
103
|
+
});
|
|
104
|
+
fs.writeFileSync(path.join(sceneDir, 'actors.yaml'), actorsYaml);
|
|
105
|
+
|
|
106
|
+
const castleDir = path.join(sceneDir, '.castle');
|
|
107
|
+
fs.mkdirSync(castleDir, { recursive: true });
|
|
108
|
+
fs.writeFileSync(path.join(castleDir, 'slug-map.json'), JSON.stringify(slugMap, null, 2));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
console.log(`Decompiled deck in ${deckDir}`);
|
|
112
|
+
} catch (err) {
|
|
113
|
+
console.error("Error during the decompilation", err);
|
|
114
|
+
}
|
|
115
|
+
}
|
package/tool/sd.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import axios from 'axios';
|
|
5
|
+
|
|
6
|
+
const npmRoot = execSync('npm root -g').toString().trim();
|
|
7
|
+
const apiPath = path.join(npmRoot, 'castle-cli', 'dist', 'api.js');
|
|
8
|
+
const API = await import(`file://${apiPath}`);
|
|
9
|
+
|
|
10
|
+
export async function saveDeck(customPath) {
|
|
11
|
+
if (!customPath) {
|
|
12
|
+
console.error("Error: Please specify a directory path.");
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const deckDir = path.resolve(customPath);
|
|
17
|
+
console.log(`Compiling deck from "${deckDir}"...`);
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const deckJsonPath = path.join(deckDir, 'deck.json');
|
|
21
|
+
if (!fs.existsSync(deckJsonPath)) {
|
|
22
|
+
console.error(`Error: deck.json not found in "${deckDir}"`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const deckData = JSON.parse(fs.readFileSync(deckJsonPath, 'utf8'));
|
|
27
|
+
const cards = deckData.cards || [];
|
|
28
|
+
if (cards.length === 0) {
|
|
29
|
+
console.error("Error: Found 0 cards in the deck.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const cardData = cards[0];
|
|
34
|
+
const cardId = cardData.cardId;
|
|
35
|
+
|
|
36
|
+
const sceneDir = path.join(deckDir, 'cards', cardId, 'scene');
|
|
37
|
+
if (!fs.existsSync(sceneDir)) {
|
|
38
|
+
fs.mkdirSync(sceneDir, { recursive: true });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const sceneJsonPath = path.join(sceneDir, 'scene.json');
|
|
42
|
+
|
|
43
|
+
// 1. Load scene.json or create default schema
|
|
44
|
+
let sceneObj;
|
|
45
|
+
if (fs.existsSync(sceneJsonPath)) {
|
|
46
|
+
sceneObj = JSON.parse(fs.readFileSync(sceneJsonPath, 'utf8'));
|
|
47
|
+
} else {
|
|
48
|
+
console.log("Warning: scene.json not found. Creating default schema...");
|
|
49
|
+
sceneObj = {
|
|
50
|
+
snapshot: {
|
|
51
|
+
actors: [],
|
|
52
|
+
library: {}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
sceneObj.snapshot = sceneObj.snapshot || {};
|
|
57
|
+
|
|
58
|
+
// 2. Compile Blueprints from blueprints/ directory
|
|
59
|
+
const blueprintsDir = path.join(sceneDir, 'blueprints');
|
|
60
|
+
const slugMapPath = path.join(sceneDir, '.castle', 'slug-map.json');
|
|
61
|
+
|
|
62
|
+
if (fs.existsSync(blueprintsDir)) {
|
|
63
|
+
sceneObj.snapshot.library = sceneObj.snapshot.library || {};
|
|
64
|
+
let slugMap = {};
|
|
65
|
+
if (fs.existsSync(slugMapPath)) {
|
|
66
|
+
slugMap = JSON.parse(fs.readFileSync(slugMapPath, 'utf8'));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const bpFiles = fs.readdirSync(blueprintsDir).filter(f => f.endsWith('.json'));
|
|
70
|
+
for (const file of bpFiles) {
|
|
71
|
+
const safeName = path.basename(file, '.json');
|
|
72
|
+
const bpContent = JSON.parse(fs.readFileSync(path.join(blueprintsDir, file), 'utf8'));
|
|
73
|
+
const uuid = bpContent.uuid || slugMap[safeName] || safeName;
|
|
74
|
+
sceneObj.snapshot.library[uuid] = bpContent;
|
|
75
|
+
}
|
|
76
|
+
console.log("Successfully compiled blueprints.");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 3. Compile Actors from actors.yaml
|
|
80
|
+
const actorsYamlPath = path.join(sceneDir, 'actors.yaml');
|
|
81
|
+
if (fs.existsSync(actorsYamlPath)) {
|
|
82
|
+
const yamlContent = fs.readFileSync(actorsYamlPath, 'utf8');
|
|
83
|
+
const actors = [];
|
|
84
|
+
let currentActor = null;
|
|
85
|
+
|
|
86
|
+
const lines = yamlContent.split('\n');
|
|
87
|
+
for (const line of lines) {
|
|
88
|
+
const trimmed = line.trim();
|
|
89
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
90
|
+
|
|
91
|
+
if (line.match(/^a\d+:/)) {
|
|
92
|
+
if (currentActor) actors.push(currentActor);
|
|
93
|
+
currentActor = {};
|
|
94
|
+
} else if (currentActor) {
|
|
95
|
+
const colonIdx = trimmed.indexOf(':');
|
|
96
|
+
if (colonIdx !== -1) {
|
|
97
|
+
const key = trimmed.substring(0, colonIdx).trim();
|
|
98
|
+
let val = trimmed.substring(colonIdx + 1).trim();
|
|
99
|
+
|
|
100
|
+
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
|
|
101
|
+
val = val.substring(1, val.length - 1);
|
|
102
|
+
}
|
|
103
|
+
if (!isNaN(val) && val !== '') {
|
|
104
|
+
val = Number(val);
|
|
105
|
+
} else if (val === 'true') val = true;
|
|
106
|
+
else if (val === 'false') val = false;
|
|
107
|
+
|
|
108
|
+
currentActor[key] = val;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (currentActor) actors.push(currentActor);
|
|
113
|
+
|
|
114
|
+
sceneObj.snapshot.actors = actors;
|
|
115
|
+
console.log("Successfully compiled actors from YAML.");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 4. Save minified scene.json
|
|
119
|
+
fs.writeFileSync(sceneJsonPath, JSON.stringify(sceneObj));
|
|
120
|
+
console.log("Updated and minified scene.json.");
|
|
121
|
+
|
|
122
|
+
// 5. Generate upload configuration
|
|
123
|
+
console.log("Generating upload configuration for scene...");
|
|
124
|
+
const uploadConfigs = await API.createSceneDataUploadConfig([cardId]);
|
|
125
|
+
const uploadConfig = uploadConfigs[0];
|
|
126
|
+
|
|
127
|
+
const sceneContent = fs.readFileSync(sceneJsonPath, 'utf8');
|
|
128
|
+
|
|
129
|
+
// 6. Upload scene.json to S3
|
|
130
|
+
console.log("Uploading scene content to Castle...");
|
|
131
|
+
const formData = new FormData();
|
|
132
|
+
for (const [key, value] of Object.entries(uploadConfig.postFields)) {
|
|
133
|
+
formData.append(key, value);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
formData.append('Content-Type', 'application/json');
|
|
137
|
+
formData.append('file', new Blob([sceneContent], { type: 'application/json' }), 'scene.json');
|
|
138
|
+
|
|
139
|
+
await axios.post(uploadConfig.postUrl, formData, {
|
|
140
|
+
maxContentLength: 100_000_000,
|
|
141
|
+
maxBodyLength: 1_000_000_000,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// 7. Submit update via GraphQL API
|
|
145
|
+
const deckInput = {
|
|
146
|
+
deckId: deckData.deckId,
|
|
147
|
+
title: deckData.title ?? "Deck",
|
|
148
|
+
visibility: deckData.visibility ?? "unlisted",
|
|
149
|
+
contentFlags: [],
|
|
150
|
+
initialCardId: cardId,
|
|
151
|
+
variables: deckData.variables ?? []
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const cardInput = {
|
|
155
|
+
cardId: cardId,
|
|
156
|
+
title: cardData.title ?? "Card",
|
|
157
|
+
blocks: [],
|
|
158
|
+
uploadId: uploadConfig.uploadId,
|
|
159
|
+
makeInitialCard: true
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const result = await API.updateCardAndDeckV2(deckInput, cardInput);
|
|
163
|
+
|
|
164
|
+
console.log("Successfully uploaded deck. Server response:");
|
|
165
|
+
console.log(JSON.stringify(result, null, 2));
|
|
166
|
+
|
|
167
|
+
} catch (err) {
|
|
168
|
+
console.error("Error while trying to executing save-deck:");
|
|
169
|
+
if (err.response && err.response.data) {
|
|
170
|
+
console.error("Details:", JSON.stringify(err.response.data, null, 2));
|
|
171
|
+
} else {
|
|
172
|
+
console.error(err.message);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|