@coze-arch/cli 0.0.37 → 0.1.2
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 +69 -104
- package/docs/deploy.md +181 -0
- package/lib/__templates__/expo/.cozeproj/scripts/dev_build.sh +4 -0
- package/lib/__templates__/expo/.cozeproj/scripts/dev_run.sh +108 -19
- package/lib/__templates__/expo/.cozeproj/scripts/prod_run.sh +1 -1
- package/lib/__templates__/expo/.cozeproj/scripts/server_dev_run.sh +1 -1
- package/lib/__templates__/expo/client/metro.config.js +3 -1
- package/lib/__templates__/expo/pnpm-lock.yaml +5 -5
- package/lib/__templates__/expo/server/package.json +2 -2
- package/lib/__templates__/expo/server/src/index.ts +1 -1
- package/lib/__templates__/expo/template.config.js +11 -2
- package/lib/__templates__/native-static/.coze +4 -2
- package/lib/__templates__/native-static/scripts/serve.ps1 +7 -0
- package/lib/__templates__/nextjs/.coze +5 -0
- package/lib/__templates__/nextjs/package.json +2 -2
- package/lib/__templates__/nextjs/pnpm-lock.yaml +5 -5
- package/lib/__templates__/nextjs/scripts/build.ps1 +16 -0
- package/lib/__templates__/nextjs/scripts/dev.ps1 +63 -0
- package/lib/__templates__/nextjs/scripts/dev.sh +234 -7
- package/lib/__templates__/nextjs/scripts/prepare.ps1 +18 -0
- package/lib/__templates__/nextjs/scripts/start.ps1 +11 -0
- package/lib/__templates__/nextjs/scripts/validate.ps1 +8 -0
- package/lib/__templates__/nextjs/template.config.js +4 -2
- package/lib/__templates__/nuxt-vue/.coze +5 -0
- package/lib/__templates__/nuxt-vue/package.json +1 -1
- package/lib/__templates__/nuxt-vue/pnpm-lock.yaml +5 -5
- package/lib/__templates__/nuxt-vue/scripts/build.ps1 +12 -0
- package/lib/__templates__/nuxt-vue/scripts/dev.ps1 +63 -0
- package/lib/__templates__/nuxt-vue/scripts/dev.sh +232 -3
- package/lib/__templates__/nuxt-vue/scripts/prepare.ps1 +12 -0
- package/lib/__templates__/nuxt-vue/scripts/start.ps1 +11 -0
- package/lib/__templates__/nuxt-vue/scripts/validate.ps1 +8 -0
- package/lib/__templates__/phaser/.coze +18 -0
- package/lib/__templates__/phaser/README.md +45 -0
- package/lib/__templates__/phaser/_gitignore +23 -0
- package/lib/__templates__/phaser/_npmrc +14 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0001.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0002.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0003.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0004.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0005.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0006.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0007.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0008.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0009.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0010.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0011.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0012.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0013.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0014.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0015.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0016.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0017.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0018.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0019.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0020.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0021.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0022.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0023.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/frame_0024.png +0 -0
- package/lib/__templates__/phaser/assets/image_sequence/coze-game-loading/manifest.json +11 -0
- package/lib/__templates__/phaser/debug-runtime/flow-graph.json +20 -0
- package/lib/__templates__/phaser/index.html +12 -0
- package/lib/__templates__/phaser/package.json +31 -0
- package/lib/__templates__/phaser/pnpm-lock.yaml +661 -0
- package/lib/__templates__/phaser/scripts/build.ps1 +12 -0
- package/lib/__templates__/phaser/scripts/build.sh +8 -0
- package/lib/__templates__/phaser/scripts/dev.ps1 +49 -0
- package/lib/__templates__/phaser/scripts/dev.sh +82 -0
- package/lib/__templates__/phaser/scripts/prepare.ps1 +8 -0
- package/lib/__templates__/phaser/scripts/prepare.sh +7 -0
- package/lib/__templates__/phaser/scripts/spawn-detached.cjs +29 -0
- package/lib/__templates__/phaser/scripts/start.ps1 +49 -0
- package/lib/__templates__/phaser/scripts/start.sh +82 -0
- package/lib/__templates__/phaser/scripts/validate.ps1 +7 -0
- package/lib/__templates__/phaser/scripts/validate.sh +7 -0
- package/lib/__templates__/phaser/src/assets.ts +15 -0
- package/lib/__templates__/phaser/src/global.d.ts +39 -0
- package/lib/__templates__/phaser/src/main.ts +28 -0
- package/lib/__templates__/phaser/src/scene/BootScene.ts +213 -0
- package/lib/__templates__/phaser/src/scene/DevLoadingScene.ts +69 -0
- package/lib/__templates__/phaser/src/style.css +28 -0
- package/lib/__templates__/phaser/src/types.ts +32 -0
- package/lib/__templates__/phaser/src/utils/asset-loader.ts +319 -0
- package/lib/__templates__/phaser/src/utils/index.ts +2 -0
- package/lib/__templates__/phaser/src/utils/mark-editable.ts +13 -0
- package/lib/__templates__/phaser/template.config.js +43 -0
- package/lib/__templates__/phaser/tsconfig.json +23 -0
- package/lib/__templates__/phaser/vite.config.ts +96 -0
- package/lib/__templates__/phaser-static/.coze +15 -0
- package/lib/__templates__/phaser-static/README.md +40 -0
- package/lib/__templates__/phaser-static/_gitignore +6 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0001.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0002.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0003.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0004.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0005.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0006.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0007.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0008.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0009.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0010.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0011.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0012.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0013.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0014.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0015.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0016.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0017.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0018.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0019.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0020.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0021.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0022.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0023.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/frame_0024.png +0 -0
- package/lib/__templates__/phaser-static/assets/image_sequence/coze-game-loading/manifest.json +11 -0
- package/lib/__templates__/phaser-static/debug-runtime/flow-graph.json +20 -0
- package/lib/__templates__/phaser-static/index.html +17 -0
- package/lib/__templates__/phaser-static/scripts/serve.ps1 +7 -0
- package/lib/__templates__/phaser-static/src/assets.js +15 -0
- package/lib/__templates__/phaser-static/src/main.js +26 -0
- package/lib/__templates__/phaser-static/src/phaser.js +7 -0
- package/lib/__templates__/phaser-static/src/scene/BootScene.js +205 -0
- package/lib/__templates__/phaser-static/src/scene/DevLoadingScene.js +67 -0
- package/lib/__templates__/phaser-static/src/style.css +28 -0
- package/lib/__templates__/phaser-static/src/utils/asset-loader.js +267 -0
- package/lib/__templates__/phaser-static/src/utils/index.js +2 -0
- package/lib/__templates__/phaser-static/src/utils/mark-editable.js +3 -0
- package/lib/__templates__/phaser-static/template.config.js +35 -0
- package/lib/__templates__/pi-agent/package.json +1 -1
- package/lib/__templates__/pi-agent/pnpm-lock.yaml +5 -5
- package/lib/__templates__/taro/.cozeproj/scripts/deploy_run.sh +1 -1
- package/lib/__templates__/taro/.cozeproj/scripts/dev_run.sh +178 -68
- package/lib/__templates__/taro/AGENTS.md +11 -7
- package/lib/__templates__/taro/README.md +4 -4
- package/lib/__templates__/taro/config/index.ts +10 -2
- package/lib/__templates__/taro/pnpm-lock.yaml +5 -5
- package/lib/__templates__/taro/server/package.json +1 -1
- package/lib/__templates__/taro/server/src/main.ts +8 -2
- package/lib/__templates__/templates.json +51 -1
- package/lib/__templates__/vite/.coze +5 -0
- package/lib/__templates__/vite/package.json +1 -1
- package/lib/__templates__/vite/pnpm-lock.yaml +5 -5
- package/lib/__templates__/vite/scripts/build.ps1 +16 -0
- package/lib/__templates__/vite/scripts/dev.ps1 +63 -0
- package/lib/__templates__/vite/scripts/dev.sh +110 -1
- package/lib/__templates__/vite/scripts/prepare.ps1 +18 -0
- package/lib/__templates__/vite/scripts/start.ps1 +11 -0
- package/lib/__templates__/vite/scripts/validate.ps1 +8 -0
- package/lib/__templates__/vite/template.config.js +4 -2
- package/lib/cli.js +3718 -211
- package/lib/deploy/package-project.js +244 -0
- package/lib/deploy/package-project.py +183 -0
- package/package.json +5 -2
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { AUDIOS, IMAGES, IMAGE_SEQUENCES, VIDEOS } from '../assets.js';
|
|
2
|
+
|
|
3
|
+
const FRAME_KEY_MARKER = ':frame:';
|
|
4
|
+
const MANIFEST_KEY_SUFFIX = ':manifest';
|
|
5
|
+
|
|
6
|
+
const getFrameKey = (assetKey, frameIndex) =>
|
|
7
|
+
assetKey +
|
|
8
|
+
FRAME_KEY_MARKER +
|
|
9
|
+
String(frameIndex + 1).padStart(4, '0');
|
|
10
|
+
|
|
11
|
+
const getManifestKey = assetKey => assetKey + MANIFEST_KEY_SUFFIX;
|
|
12
|
+
|
|
13
|
+
const getFrameSource = (directory, frameIndex) =>
|
|
14
|
+
directory + '/frame_' + String(frameIndex + 1).padStart(4, '0') + '.png';
|
|
15
|
+
|
|
16
|
+
export const resolveAssetUrl = source => new URL(source, document.baseURI).href;
|
|
17
|
+
|
|
18
|
+
const isImageSequenceManifest = value => {
|
|
19
|
+
if (!value || typeof value !== 'object') {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
value.type === 'image_sequence' &&
|
|
25
|
+
typeof value.name === 'string' &&
|
|
26
|
+
typeof value.properties?.fps === 'number' &&
|
|
27
|
+
typeof value.properties.frame_count === 'number' &&
|
|
28
|
+
value.properties.frame_count > 0
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const getImageSequenceManifest = (scene, assetKey) => {
|
|
33
|
+
const manifest = scene.cache.json.get(getManifestKey(assetKey));
|
|
34
|
+
if (!isImageSequenceManifest(manifest)) {
|
|
35
|
+
throw new Error('Invalid image sequence manifest: ' + assetKey);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return manifest;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const queueImageSequenceFrames = (scene, assetKey, directory, manifest) => {
|
|
42
|
+
Array.from(
|
|
43
|
+
{ length: manifest.properties.frame_count },
|
|
44
|
+
(_value, frameIndex) => {
|
|
45
|
+
scene.load.image(
|
|
46
|
+
getFrameKey(assetKey, frameIndex),
|
|
47
|
+
resolveAssetUrl(getFrameSource(directory, frameIndex)),
|
|
48
|
+
);
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const queueImages = (scene, images) => {
|
|
54
|
+
Object.entries(images).forEach(([assetKey, source]) => {
|
|
55
|
+
scene.load.image(assetKey, resolveAssetUrl(source));
|
|
56
|
+
});
|
|
57
|
+
return Object.keys(images).length;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const queueVideos = (scene, videos) => {
|
|
61
|
+
Object.entries(videos).forEach(([assetKey, source]) => {
|
|
62
|
+
scene.load.video(assetKey, resolveAssetUrl(source));
|
|
63
|
+
});
|
|
64
|
+
return Object.keys(videos).length;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const queueAudios = (scene, audios) => {
|
|
68
|
+
Object.entries(audios).forEach(([assetKey, source]) => {
|
|
69
|
+
scene.load.audio(assetKey, resolveAssetUrl(source));
|
|
70
|
+
});
|
|
71
|
+
return Object.keys(audios).length;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const queueImageSequences = (scene, imageSequences) =>
|
|
75
|
+
Object.entries(imageSequences).reduce(
|
|
76
|
+
(fileCount, [assetKey, directory]) => {
|
|
77
|
+
const manifestKey = getManifestKey(assetKey);
|
|
78
|
+
const cachedManifest = scene.cache.json.get(manifestKey);
|
|
79
|
+
|
|
80
|
+
if (isImageSequenceManifest(cachedManifest)) {
|
|
81
|
+
queueImageSequenceFrames(scene, assetKey, directory, cachedManifest);
|
|
82
|
+
return fileCount + cachedManifest.properties.frame_count;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
scene.load.once(
|
|
86
|
+
'filecomplete-json-' + manifestKey,
|
|
87
|
+
(_key, _type, data) => {
|
|
88
|
+
if (!isImageSequenceManifest(data)) {
|
|
89
|
+
throw new Error('Invalid image sequence manifest: ' + assetKey);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
queueImageSequenceFrames(scene, assetKey, directory, data);
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
scene.load.json(
|
|
97
|
+
manifestKey,
|
|
98
|
+
resolveAssetUrl(directory + '/manifest.json'),
|
|
99
|
+
);
|
|
100
|
+
return fileCount + 1;
|
|
101
|
+
},
|
|
102
|
+
0,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
export const loadAssets = (scene, collections) =>
|
|
106
|
+
queueImages(scene, collections.images ?? {}) +
|
|
107
|
+
queueImageSequences(scene, collections.imageSequences ?? {}) +
|
|
108
|
+
queueVideos(scene, collections.videos ?? {}) +
|
|
109
|
+
queueAudios(scene, collections.audios ?? {});
|
|
110
|
+
|
|
111
|
+
export const getImageSequenceRuntimeKeys = assetKey => ({
|
|
112
|
+
texture: getFrameKey(assetKey, 0),
|
|
113
|
+
animation: assetKey + ':animation',
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
export const getAssetKeyFromFileKey = fileKey =>
|
|
117
|
+
fileKey.split(FRAME_KEY_MARKER)[0]?.replace(MANIFEST_KEY_SUFFIX, '') ??
|
|
118
|
+
fileKey;
|
|
119
|
+
|
|
120
|
+
export const getAssetLabelFromFileKey = (fileKey, collections) => {
|
|
121
|
+
const assetKey = getAssetKeyFromFileKey(fileKey);
|
|
122
|
+
|
|
123
|
+
if (assetKey in (collections.images ?? {})) {
|
|
124
|
+
return '图片 ' + assetKey;
|
|
125
|
+
}
|
|
126
|
+
if (assetKey in (collections.imageSequences ?? {})) {
|
|
127
|
+
return '帧动画 ' + assetKey;
|
|
128
|
+
}
|
|
129
|
+
if (assetKey in (collections.videos ?? {})) {
|
|
130
|
+
return '视频 ' + assetKey;
|
|
131
|
+
}
|
|
132
|
+
if (assetKey in (collections.audios ?? {})) {
|
|
133
|
+
return '音频 ' + assetKey;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return assetKey;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export const registerImageSequenceAnimations = (scene, imageSequences) => {
|
|
140
|
+
Object.keys(imageSequences).forEach(assetKey => {
|
|
141
|
+
const manifest = getImageSequenceManifest(scene, assetKey);
|
|
142
|
+
const animationKey = getImageSequenceRuntimeKeys(assetKey).animation;
|
|
143
|
+
if (scene.anims.exists(animationKey)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
scene.anims.create({
|
|
148
|
+
key: animationKey,
|
|
149
|
+
frames: Array.from(
|
|
150
|
+
{ length: manifest.properties.frame_count },
|
|
151
|
+
(_value, frameIndex) => ({ key: getFrameKey(assetKey, frameIndex) }),
|
|
152
|
+
),
|
|
153
|
+
frameRate: manifest.properties.fps,
|
|
154
|
+
repeat: -1,
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const findAsset = assetKey => {
|
|
160
|
+
const matches = [];
|
|
161
|
+
|
|
162
|
+
if (Object.hasOwn(IMAGES, assetKey)) {
|
|
163
|
+
matches.push({
|
|
164
|
+
collections: { images: { [assetKey]: IMAGES[assetKey] } },
|
|
165
|
+
loadedAsset: { kind: 'image', key: assetKey },
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (Object.hasOwn(IMAGE_SEQUENCES, assetKey)) {
|
|
169
|
+
matches.push({
|
|
170
|
+
collections: {
|
|
171
|
+
imageSequences: { [assetKey]: IMAGE_SEQUENCES[assetKey] },
|
|
172
|
+
},
|
|
173
|
+
loadedAsset: {
|
|
174
|
+
kind: 'image-sequence',
|
|
175
|
+
key: assetKey,
|
|
176
|
+
...getImageSequenceRuntimeKeys(assetKey),
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (Object.hasOwn(VIDEOS, assetKey)) {
|
|
181
|
+
matches.push({
|
|
182
|
+
collections: { videos: { [assetKey]: VIDEOS[assetKey] } },
|
|
183
|
+
loadedAsset: { kind: 'video', key: assetKey },
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
if (Object.hasOwn(AUDIOS, assetKey)) {
|
|
187
|
+
matches.push({
|
|
188
|
+
collections: { audios: { [assetKey]: AUDIOS[assetKey] } },
|
|
189
|
+
loadedAsset: { kind: 'audio', key: assetKey },
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (matches.length === 0) {
|
|
194
|
+
throw new Error('Asset not found: ' + assetKey);
|
|
195
|
+
}
|
|
196
|
+
if (matches.length > 1) {
|
|
197
|
+
throw new Error('Duplicate asset key: ' + assetKey);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return matches[0];
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const isAssetLoaded = (scene, loadedAsset) => {
|
|
204
|
+
switch (loadedAsset.kind) {
|
|
205
|
+
case 'image':
|
|
206
|
+
return scene.textures.exists(loadedAsset.key);
|
|
207
|
+
case 'image-sequence':
|
|
208
|
+
return scene.anims.exists(loadedAsset.animation);
|
|
209
|
+
case 'video':
|
|
210
|
+
return scene.cache.video.exists(loadedAsset.key);
|
|
211
|
+
case 'audio':
|
|
212
|
+
return scene.cache.audio.exists(loadedAsset.key);
|
|
213
|
+
default:
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
export const loadAsset = async (scene, assetKey) => {
|
|
219
|
+
const { collections, loadedAsset } = findAsset(assetKey);
|
|
220
|
+
if (isAssetLoaded(scene, loadedAsset)) {
|
|
221
|
+
return loadedAsset;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return new Promise((resolve, reject) => {
|
|
225
|
+
const cleanup = () => {
|
|
226
|
+
scene.load.off('complete', onComplete);
|
|
227
|
+
scene.load.off('loaderror', onLoadError);
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const onComplete = () => {
|
|
231
|
+
cleanup();
|
|
232
|
+
try {
|
|
233
|
+
if (loadedAsset.kind === 'image-sequence') {
|
|
234
|
+
registerImageSequenceAnimations(
|
|
235
|
+
scene,
|
|
236
|
+
collections.imageSequences ?? {},
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
resolve(loadedAsset);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
reject(error);
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const onLoadError = file => {
|
|
246
|
+
if (getAssetKeyFromFileKey(file.key) !== assetKey) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
cleanup();
|
|
251
|
+
reject(new Error('Failed to load asset: ' + assetKey));
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
scene.load.once('complete', onComplete);
|
|
255
|
+
scene.load.on('loaderror', onLoadError);
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
loadAssets(scene, collections);
|
|
259
|
+
if (!scene.load.isLoading()) {
|
|
260
|
+
scene.load.start();
|
|
261
|
+
}
|
|
262
|
+
} catch (error) {
|
|
263
|
+
cleanup();
|
|
264
|
+
reject(error);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export const paramsSchema = {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
appName: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
minLength: 1,
|
|
15
|
+
pattern: '^[a-z0-9-]+$',
|
|
16
|
+
description:
|
|
17
|
+
'Application name (lowercase, alphanumeric and hyphens only)',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
required: [],
|
|
21
|
+
additionalProperties: false,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const config = {
|
|
25
|
+
description:
|
|
26
|
+
'Phaser Static(纯静态 2D 游戏):coze-dev init ${COZE_WORKSPACE_PATH} --template phaser-static\n' +
|
|
27
|
+
'- 适用:浏览器原生 JavaScript ESM 的 Phaser 3 游戏。\n' +
|
|
28
|
+
'- Phaser 与 Phaser Bridge 从 CDN 加载;无需 npm、Vite 或 TypeScript。',
|
|
29
|
+
paramsSchema,
|
|
30
|
+
defaultParams: {
|
|
31
|
+
appName: 'phaser-static-game',
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default config;
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@mariozechner/pi-ai": "^0.63.2",
|
|
23
23
|
"@mariozechner/pi-coding-agent": "^0.63.2",
|
|
24
24
|
"@sinclair/typebox": "^0.34.48",
|
|
25
|
-
"coze-coding-dev-sdk": "^0.7.
|
|
25
|
+
"coze-coding-dev-sdk": "^0.7.25",
|
|
26
26
|
"@radix-ui/react-select": "^2.2.6",
|
|
27
27
|
"@radix-ui/react-slot": "^1.2.4",
|
|
28
28
|
"@streamdown/code": "^1.1.1",
|
|
@@ -36,8 +36,8 @@ importers:
|
|
|
36
36
|
specifier: ^2.1.1
|
|
37
37
|
version: 2.1.1
|
|
38
38
|
coze-coding-dev-sdk:
|
|
39
|
-
specifier: ^0.7.
|
|
40
|
-
version: 0.7.
|
|
39
|
+
specifier: ^0.7.25
|
|
40
|
+
version: 0.7.25(openai@6.34.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
|
|
41
41
|
express:
|
|
42
42
|
specifier: ^4.21.2
|
|
43
43
|
version: 4.22.1
|
|
@@ -2085,8 +2085,8 @@ packages:
|
|
|
2085
2085
|
cose-base@2.2.0:
|
|
2086
2086
|
resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==}
|
|
2087
2087
|
|
|
2088
|
-
coze-coding-dev-sdk@0.7.
|
|
2089
|
-
resolution: {integrity: sha512-
|
|
2088
|
+
coze-coding-dev-sdk@0.7.25:
|
|
2089
|
+
resolution: {integrity: sha512-5683ngikMdsRW0slGTE2OAZ1Fyj0lhkC4fGNOwsqKc35e68/gtg9KmmtK9CK9SZ+5/oIn/4wLoHXFB0gHJtO2A==}
|
|
2090
2090
|
engines: {node: '>=18.0.0'}
|
|
2091
2091
|
hasBin: true
|
|
2092
2092
|
|
|
@@ -6120,7 +6120,7 @@ snapshots:
|
|
|
6120
6120
|
dependencies:
|
|
6121
6121
|
layout-base: 2.0.1
|
|
6122
6122
|
|
|
6123
|
-
coze-coding-dev-sdk@0.7.
|
|
6123
|
+
coze-coding-dev-sdk@0.7.25(openai@6.34.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0):
|
|
6124
6124
|
dependencies:
|
|
6125
6125
|
'@langchain/core': 1.1.40(openai@6.34.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
|
|
6126
6126
|
'@langchain/openai': 1.4.3(@langchain/core@1.1.40(openai@6.34.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(ws@8.20.0)
|
|
@@ -4,7 +4,7 @@ set -Eeuo pipefail
|
|
|
4
4
|
start_service() {
|
|
5
5
|
cd "${COZE_WORKSPACE_PATH}/server/dist"
|
|
6
6
|
|
|
7
|
-
local port="${DEPLOY_RUN_PORT
|
|
7
|
+
local port="${DEPLOY_RUN_PORT:-<%= serverPort %>}"
|
|
8
8
|
echo "Starting Static File Server on port ${port} for deploy..."
|
|
9
9
|
|
|
10
10
|
node ./main.js -p "${port}"
|
|
@@ -2,12 +2,17 @@
|
|
|
2
2
|
echo "⚙️ dev_run.sh 开始运行"
|
|
3
3
|
set -Eeuo pipefail
|
|
4
4
|
|
|
5
|
+
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
6
|
+
COZE_WORKSPACE_PATH="${COZE_WORKSPACE_PATH:-${ROOT_DIR}}"
|
|
5
7
|
cd "${COZE_WORKSPACE_PATH}"
|
|
6
8
|
|
|
7
9
|
# ---------------------------------------------------------
|
|
8
|
-
#
|
|
10
|
+
# 项目级日志目录
|
|
9
11
|
# ---------------------------------------------------------
|
|
10
|
-
|
|
12
|
+
LOG_DIR="${COZE_WORKSPACE_PATH}/logs"
|
|
13
|
+
LOG_FILE="${LOG_DIR}/dev.log"
|
|
14
|
+
PID_FILE="${LOG_DIR}/dev.pid"
|
|
15
|
+
DEV_PID=""
|
|
11
16
|
|
|
12
17
|
# ---------------------------------------------------------
|
|
13
18
|
# 工具函数
|
|
@@ -25,99 +30,192 @@ kill_process_tree() {
|
|
|
25
30
|
fi
|
|
26
31
|
}
|
|
27
32
|
|
|
33
|
+
# detached 出去的进程没人负责回收,超过这个时长就自己退出,避免端口与内存长期泄露。
|
|
34
|
+
MAX_RUNTIME_SECONDS=1200
|
|
35
|
+
|
|
36
|
+
# 真正被 detach 的是这层 bash wrapper:它是进程组 leader,组内 watchdog 到点回收整组
|
|
37
|
+
# (wrapper -> pnpm -> taro/nest);被包的进程自己先退出时也顺手清空进程组,不留残余。
|
|
38
|
+
RUN_WITH_TIMEOUT='
|
|
39
|
+
timeout_seconds=$1
|
|
40
|
+
shift
|
|
41
|
+
|
|
42
|
+
"$@" &
|
|
43
|
+
child_pid=$!
|
|
44
|
+
|
|
45
|
+
# 先忽略 TERM,才能在向整组发 TERM(自己也在组里)之后存活下来补一发 KILL。
|
|
46
|
+
( trap "" TERM
|
|
47
|
+
sleep "${timeout_seconds}"
|
|
48
|
+
echo "[dev] 后台进程运行超过 ${timeout_seconds}s,回收进程组 $$。"
|
|
49
|
+
kill -TERM -- "-$$" 2>/dev/null || true
|
|
50
|
+
sleep 5
|
|
51
|
+
kill -KILL -- "-$$" 2>/dev/null || true
|
|
52
|
+
) &
|
|
53
|
+
|
|
54
|
+
wait "${child_pid}"
|
|
55
|
+
kill -KILL -- "-$$" 2>/dev/null || true
|
|
56
|
+
'
|
|
57
|
+
|
|
58
|
+
# coze-daemon 会在 runtime shell 退出时清理原进程组。
|
|
59
|
+
# 通过 Node detached spawn 创建独立 session/进程组,并将 stdio 直接写入日志。
|
|
60
|
+
# 返回的 PID 是 wrapper 的,同时也是整个进程组的 PGID,后续按组回收。
|
|
61
|
+
spawn_detached() {
|
|
62
|
+
local cwd="$1"
|
|
63
|
+
local log_file="$2"
|
|
64
|
+
shift 2
|
|
65
|
+
|
|
66
|
+
node - "$cwd" "$log_file" \
|
|
67
|
+
/bin/bash -c "${RUN_WITH_TIMEOUT}" detached-runner "${MAX_RUNTIME_SECONDS}" "$@" <<'NODE'
|
|
68
|
+
const fs = require('node:fs');
|
|
69
|
+
const { spawn } = require('node:child_process');
|
|
70
|
+
|
|
71
|
+
const [cwd, logFile, command, ...args] = process.argv.slice(2);
|
|
72
|
+
if (!cwd || !logFile || !command) {
|
|
73
|
+
throw new Error('spawn_detached 缺少 cwd、log_file 或 command');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const logFd = fs.openSync(logFile, 'a');
|
|
77
|
+
try {
|
|
78
|
+
const child = spawn(command, args, {
|
|
79
|
+
cwd,
|
|
80
|
+
detached: true,
|
|
81
|
+
env: process.env,
|
|
82
|
+
stdio: ['ignore', logFd, logFd],
|
|
83
|
+
});
|
|
84
|
+
child.unref();
|
|
85
|
+
process.stdout.write(String(child.pid));
|
|
86
|
+
} finally {
|
|
87
|
+
fs.closeSync(logFd);
|
|
88
|
+
}
|
|
89
|
+
NODE
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
stop_detached() {
|
|
93
|
+
local pid="${1:-}"
|
|
94
|
+
if [[ -z "${pid}" ]]; then
|
|
95
|
+
return
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
kill -TERM -- "-${pid}" 2>/dev/null || kill -TERM "${pid}" 2>/dev/null || true
|
|
99
|
+
sleep 1
|
|
100
|
+
kill -KILL -- "-${pid}" 2>/dev/null || true
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
process_belongs_to_workspace() {
|
|
104
|
+
local pid=$1
|
|
105
|
+
local cwd=""
|
|
106
|
+
|
|
107
|
+
if [[ -L "/proc/${pid}/cwd" ]]; then
|
|
108
|
+
cwd=$(readlink "/proc/${pid}/cwd" 2>/dev/null || true)
|
|
109
|
+
elif command -v lsof >/dev/null 2>&1; then
|
|
110
|
+
cwd=$(lsof -a -p "${pid}" -d cwd -Fn 2>/dev/null | sed -n 's/^n//p' | head -1)
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
case "${cwd}" in
|
|
114
|
+
"${COZE_WORKSPACE_PATH}"|"${COZE_WORKSPACE_PATH}"/*) return 0 ;;
|
|
115
|
+
*) return 1 ;;
|
|
116
|
+
esac
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
process_group_belongs_to_workspace() {
|
|
120
|
+
local pgid=$1
|
|
121
|
+
local pids
|
|
122
|
+
local found=0
|
|
123
|
+
|
|
124
|
+
pids=$(ps -axo pid=,pgid= 2>/dev/null | awk -v pgid="${pgid}" '$2 == pgid {print $1}')
|
|
125
|
+
for pid in ${pids}; do
|
|
126
|
+
found=1
|
|
127
|
+
if ! process_belongs_to_workspace "${pid}"; then
|
|
128
|
+
return 1
|
|
129
|
+
fi
|
|
130
|
+
done
|
|
131
|
+
|
|
132
|
+
[[ "${found}" -eq 1 ]]
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# 优先用 ss,无结果或缺少 ss 时回落到 lsof;两者都不可用则显式告警,避免静默误判为端口空闲
|
|
136
|
+
list_port_pids() {
|
|
137
|
+
local port=$1
|
|
138
|
+
local pids=""
|
|
139
|
+
local has_tool=0
|
|
140
|
+
|
|
141
|
+
if command -v ss >/dev/null 2>&1; then
|
|
142
|
+
has_tool=1
|
|
143
|
+
pids=$(ss -H -lntp 2>/dev/null | awk -v port="${port}" '$4 ~ ":"port"$"' | grep -o 'pid=[0-9]*' | cut -d= -f2 | sort -u | paste -sd' ' - || true)
|
|
144
|
+
fi
|
|
145
|
+
if [[ -z "${pids}" ]] && command -v lsof >/dev/null 2>&1; then
|
|
146
|
+
has_tool=1
|
|
147
|
+
pids=$(lsof -t -iTCP:"${port}" -sTCP:LISTEN 2>/dev/null | sort -u | paste -sd' ' - || true)
|
|
148
|
+
fi
|
|
149
|
+
if [[ "${has_tool}" -eq 0 ]]; then
|
|
150
|
+
echo "Warning: neither ss nor lsof available, cannot inspect port ${port}." >&2
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
echo "${pids}"
|
|
154
|
+
}
|
|
155
|
+
|
|
28
156
|
kill_port_if_listening() {
|
|
29
157
|
local port=$1
|
|
30
158
|
local pids
|
|
31
|
-
pids=$(
|
|
159
|
+
pids=$(list_port_pids "${port}")
|
|
32
160
|
if [[ -z "${pids}" ]]; then
|
|
33
161
|
echo "Port ${port} is free."
|
|
34
162
|
return
|
|
35
163
|
fi
|
|
36
|
-
echo "Port ${port} in use by PIDs: ${pids}"
|
|
164
|
+
echo "Port ${port} in use by PIDs: ${pids} (SIGKILL)"
|
|
37
165
|
for pid in ${pids}; do
|
|
38
166
|
kill_process_tree "${pid}"
|
|
39
167
|
done
|
|
40
168
|
sleep 1
|
|
41
|
-
pids=$(
|
|
169
|
+
pids=$(list_port_pids "${port}")
|
|
42
170
|
if [[ -n "${pids}" ]]; then
|
|
43
|
-
echo "Warning: port ${port} still busy after
|
|
171
|
+
echo "Warning: port ${port} still busy after SIGKILL, PIDs: ${pids}"
|
|
44
172
|
else
|
|
45
173
|
echo "Port ${port} cleared."
|
|
46
174
|
fi
|
|
47
175
|
}
|
|
48
176
|
|
|
49
|
-
#
|
|
50
|
-
# 1. 清理上一次运行残留的整棵进程树
|
|
51
|
-
# ---------------------------------------------------------
|
|
177
|
+
# 只清理当前项目上次记录的 detached 进程。
|
|
52
178
|
cleanup_previous_run() {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
local old_pid
|
|
56
|
-
old_pid=$(cat "${PID_FILE}" 2>/dev/null || true)
|
|
57
|
-
if [[ -n "${old_pid}" ]] && kill -0 "${old_pid}" 2>/dev/null; then
|
|
58
|
-
echo "🧹 Killing previous dev process tree (root PID: ${old_pid})..."
|
|
59
|
-
kill_process_tree "${old_pid}"
|
|
60
|
-
fi
|
|
61
|
-
rm -f "${PID_FILE}"
|
|
179
|
+
if [[ ! -f "${PID_FILE}" ]]; then
|
|
180
|
+
return
|
|
62
181
|
fi
|
|
63
182
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
"node --enable-source-maps.*/workspace/projects/server/dist/main"
|
|
72
|
-
"esbuild --service.*--ping"
|
|
73
|
-
)
|
|
74
|
-
for pattern in "${patterns[@]}"; do
|
|
75
|
-
local pids
|
|
76
|
-
pids=$(pgrep -f "${pattern}" 2>/dev/null || true)
|
|
77
|
-
for pid in ${pids}; do
|
|
78
|
-
# 不杀自己和自己的父进程链
|
|
79
|
-
if [[ "${pid}" != "$$" ]] && [[ "${pid}" != "${PPID}" ]]; then
|
|
80
|
-
echo " Killing orphan PID ${pid} matching '${pattern}'"
|
|
81
|
-
kill -9 "${pid}" 2>/dev/null || true
|
|
82
|
-
fi
|
|
83
|
-
done
|
|
84
|
-
done
|
|
85
|
-
sleep 1
|
|
183
|
+
local old_pid
|
|
184
|
+
old_pid=$(cat "${PID_FILE}" 2>/dev/null || true)
|
|
185
|
+
if [[ "${old_pid}" =~ ^[1-9][0-9]*$ ]] && process_group_belongs_to_workspace "${old_pid}"; then
|
|
186
|
+
echo "🧹 Killing previous dev process group (PGID: ${old_pid})..."
|
|
187
|
+
stop_detached "${old_pid}"
|
|
188
|
+
fi
|
|
189
|
+
rm -f "${PID_FILE}"
|
|
86
190
|
}
|
|
87
191
|
|
|
88
192
|
# ---------------------------------------------------------
|
|
89
193
|
# 2. 安装依赖
|
|
90
194
|
# ---------------------------------------------------------
|
|
91
195
|
echo "📦 Installing dependencies..."
|
|
92
|
-
pnpm
|
|
196
|
+
PNPM_BIN="$(command -v pnpm)"
|
|
197
|
+
"${PNPM_BIN}" install
|
|
93
198
|
echo "✅ Dependencies installed successfully!"
|
|
94
199
|
|
|
95
200
|
# ---------------------------------------------------------
|
|
96
201
|
# 3. 清理旧进程 + 端口
|
|
97
202
|
# ---------------------------------------------------------
|
|
98
|
-
|
|
203
|
+
# DEPLOY_RUN_PORT 是平台注入的对外端口,项目内统一使用 PORT。
|
|
204
|
+
PORT="${DEPLOY_RUN_PORT:-${PORT:-<%= port %>}}"
|
|
205
|
+
SERVER_PORT="${SERVER_PORT:-<%= serverPort %>}"
|
|
206
|
+
export PORT
|
|
207
|
+
export SERVER_PORT
|
|
99
208
|
|
|
209
|
+
mkdir -p "${LOG_DIR}"
|
|
100
210
|
cleanup_previous_run
|
|
101
211
|
|
|
102
|
-
echo "Clearing port ${
|
|
103
|
-
kill_port_if_listening "${
|
|
212
|
+
echo "Clearing port ${PORT} (web) before start."
|
|
213
|
+
kill_port_if_listening "${PORT}"
|
|
104
214
|
echo "Clearing port ${SERVER_PORT} (server) before start."
|
|
105
215
|
kill_port_if_listening "${SERVER_PORT}"
|
|
106
216
|
|
|
107
217
|
# ---------------------------------------------------------
|
|
108
|
-
# 4.
|
|
109
|
-
# ---------------------------------------------------------
|
|
110
|
-
cleanup_on_exit() {
|
|
111
|
-
echo "🛑 dev_run.sh exiting, cleaning up child processes..."
|
|
112
|
-
# 杀掉当前脚本的所有子进程
|
|
113
|
-
kill -- -$$ 2>/dev/null || true
|
|
114
|
-
rm -f "${PID_FILE}"
|
|
115
|
-
exit 0
|
|
116
|
-
}
|
|
117
|
-
trap cleanup_on_exit EXIT INT TERM HUP
|
|
118
|
-
|
|
119
|
-
# ---------------------------------------------------------
|
|
120
|
-
# 5. 启动服务
|
|
218
|
+
# 4. 启动服务
|
|
121
219
|
# ---------------------------------------------------------
|
|
122
220
|
start_service() {
|
|
123
221
|
cd "${COZE_WORKSPACE_PATH}"
|
|
@@ -133,19 +231,31 @@ start_service() {
|
|
|
133
231
|
# 启动 Taro H5 和 NestJS Server
|
|
134
232
|
echo "Starting Taro H5 Dev Server and NestJS Server..."
|
|
135
233
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
234
|
+
: > "${LOG_FILE}"
|
|
235
|
+
|
|
236
|
+
DEV_PID="$(spawn_detached \
|
|
237
|
+
"${COZE_WORKSPACE_PATH}" \
|
|
238
|
+
"${LOG_FILE}" \
|
|
239
|
+
"${PNPM_BIN}" dev)"
|
|
240
|
+
if [[ -z "${DEV_PID}" ]]; then
|
|
241
|
+
echo "❌ 无法获取 dev 后台进程 PID"
|
|
242
|
+
return 1
|
|
243
|
+
fi
|
|
244
|
+
echo "${DEV_PID}" > "${PID_FILE}"
|
|
139
245
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
246
|
+
sleep 1
|
|
247
|
+
if ! kill -0 "${DEV_PID}" 2>/dev/null; then
|
|
248
|
+
echo "❌ Dev service failed to start. See ${LOG_FILE}." >&2
|
|
249
|
+
tail -n 20 "${LOG_FILE}" >&2 || true
|
|
250
|
+
rm -f "${PID_FILE}"
|
|
251
|
+
return 1
|
|
252
|
+
fi
|
|
145
253
|
|
|
146
|
-
|
|
147
|
-
|
|
254
|
+
echo "📝 Dev process started with PID: ${DEV_PID}"
|
|
255
|
+
echo "Auto stop after ${MAX_RUNTIME_SECONDS}s."
|
|
256
|
+
echo "Log file: ${LOG_FILE}"
|
|
257
|
+
echo "PID file: ${PID_FILE}"
|
|
148
258
|
}
|
|
149
259
|
|
|
150
|
-
echo "Starting HTTP services on port ${
|
|
260
|
+
echo "Starting HTTP services on port ${PORT} (web) and ${SERVER_PORT} (server)..."
|
|
151
261
|
start_service
|