@playcraft/cli 0.1.4 → 0.1.5
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.
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { createHash, randomUUID } from 'node:crypto';
|
|
3
|
-
import { appendFile, lstat, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { appendFile, lstat, mkdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
4
5
|
import { join } from 'node:path';
|
|
5
6
|
import { startLauncherProcess } from '../codex/launcher-client.js';
|
|
6
7
|
/** Long enough for a cold store on a game template; a stuck registry must not hold the write lease. */
|
|
@@ -8,6 +9,7 @@ const INSTALL_TIMEOUT_MS = 10 * 60_000;
|
|
|
8
9
|
const LOG_TAIL_LINES = 50;
|
|
9
10
|
const LOG_TAIL_MAX_CHARS = 8_000;
|
|
10
11
|
const WRITE_PREFIX = 'dependencies-';
|
|
12
|
+
const SHARE_WAIT_MS = 60_000;
|
|
11
13
|
/**
|
|
12
14
|
* Projects are managed with pnpm. It follows pnpm-lock.yaml when the lockfile matches, never
|
|
13
15
|
* prompts, and writes plain lines to the log. Package scripts run: the workspace is the user's own
|
|
@@ -102,7 +104,12 @@ async function installOnce(state, workspaceId, root) {
|
|
|
102
104
|
});
|
|
103
105
|
let outcome;
|
|
104
106
|
try {
|
|
105
|
-
|
|
107
|
+
// Through the launcher the install runs as the development group, which cannot enter the
|
|
108
|
+
// checkout until Runtime has opened it; a local install runs as the owner and need not wait.
|
|
109
|
+
const shared = !launcherSocket(state) || (await waitUntilShared(root));
|
|
110
|
+
outcome = shared
|
|
111
|
+
? await runInstall(state, workspaceId, root)
|
|
112
|
+
: { code: undefined, timedOut: false, log: '', error: 'workspace_not_shared' };
|
|
106
113
|
}
|
|
107
114
|
finally {
|
|
108
115
|
await state.journal.putManagedWrite({
|
|
@@ -119,7 +126,10 @@ async function installOnce(state, workspaceId, root) {
|
|
|
119
126
|
status: ok ? 'ready' : 'failed',
|
|
120
127
|
...(ok
|
|
121
128
|
? { lockHash: plan.lockHash }
|
|
122
|
-
: {
|
|
129
|
+
: {
|
|
130
|
+
error: outcome.error ??
|
|
131
|
+
(outcome.timedOut ? 'install_timeout' : `install_exit_${outcome.code ?? 'lost'}`),
|
|
132
|
+
}),
|
|
123
133
|
startedAt,
|
|
124
134
|
finishedAt: new Date().toISOString(),
|
|
125
135
|
logTail: tail(outcome.log),
|
|
@@ -144,8 +154,8 @@ async function runInstall(state, workspaceId, root) {
|
|
|
144
154
|
log = (log + text).slice(-64 * 1024);
|
|
145
155
|
pending = pending.then(() => appendFile(logPath, text)).catch(() => undefined);
|
|
146
156
|
};
|
|
147
|
-
const socketPath =
|
|
148
|
-
const launched = socketPath
|
|
157
|
+
const socketPath = launcherSocket(state);
|
|
158
|
+
const launched = socketPath
|
|
149
159
|
? await startLauncherProcess({
|
|
150
160
|
socketPath,
|
|
151
161
|
processId: `${WRITE_PREFIX}${workspaceId}`,
|
|
@@ -196,6 +206,29 @@ async function runInstall(state, workspaceId, root) {
|
|
|
196
206
|
await pending;
|
|
197
207
|
return { code, timedOut, log };
|
|
198
208
|
}
|
|
209
|
+
function launcherSocket(state) {
|
|
210
|
+
const socketPath = process.env.PLAYCRAFT_LAUNCHER_SOCKET;
|
|
211
|
+
return socketPath && state.config.workspaceMode === 'multi' ? socketPath : undefined;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* The checkout is cloned private to the control user and opened to the development group by
|
|
215
|
+
* Runtime after registration. Group read, write and search on the root is the sign it is open.
|
|
216
|
+
*/
|
|
217
|
+
export async function waitUntilShared(root, timeoutMs = SHARE_WAIT_MS, intervalMs = 500) {
|
|
218
|
+
const deadline = Date.now() + timeoutMs;
|
|
219
|
+
for (;;) {
|
|
220
|
+
try {
|
|
221
|
+
if (((await stat(root)).mode & 0o070) === 0o070)
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
/* not there yet */
|
|
226
|
+
}
|
|
227
|
+
if (Date.now() >= deadline)
|
|
228
|
+
return false;
|
|
229
|
+
await delay(intervalMs);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
199
232
|
/** A local-only ignore, never committed: the change list must not fill up with installed packages. */
|
|
200
233
|
async function excludeFromGit(root) {
|
|
201
234
|
const info = join(root, '.git', 'info');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playcraft/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
"@gltf-transform/core": "^4.3.0",
|
|
49
49
|
"@gltf-transform/extensions": "^4.3.0",
|
|
50
50
|
"@gltf-transform/functions": "^4.3.0",
|
|
51
|
-
"@playcraft/build": "^0.1.
|
|
52
|
-
"@playcraft/common": "^0.1.
|
|
51
|
+
"@playcraft/build": "^0.1.5",
|
|
52
|
+
"@playcraft/common": "^0.1.5",
|
|
53
53
|
"better-sqlite3-pinned": "npm:better-sqlite3@13.0.3",
|
|
54
54
|
"chokidar": "^4.0.3",
|
|
55
55
|
"commander": "^13.1.0",
|