feat: refine wooden fish runtime generation
This commit is contained in:
@@ -816,7 +816,28 @@ class DevRunner {
|
||||
console.log(`[dev:spacetime] 迁移引导密钥: ${this.options.migrationBootstrapSecret}`);
|
||||
}
|
||||
|
||||
startApiServer(service) {
|
||||
async ensureApiServerSpacetimeToken() {
|
||||
const existingToken = String(this.baseEnv.GENARRATIVE_SPACETIME_TOKEN ?? '').trim();
|
||||
if (existingToken && shouldTrustExistingSpacetimeToken(existingToken, this.state.spacetimeServer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const identityUrl = buildUrl(this.state.spacetimeServer, '/v1/identity');
|
||||
if (!identityUrl) {
|
||||
throw new Error(`无法构造 SpacetimeDB identity 地址: ${this.state.spacetimeServer}`);
|
||||
}
|
||||
|
||||
const response = await fetchSpacetimeIdentity(identityUrl);
|
||||
this.baseEnv.GENARRATIVE_SPACETIME_TOKEN = response.token;
|
||||
this.state.spacetimeIdentity = response.identity;
|
||||
console.log(
|
||||
`[dev:spacetime] 已创建本地 Web identity: ${response.identity.slice(0, 12)}...`,
|
||||
);
|
||||
}
|
||||
|
||||
async startApiServer(service) {
|
||||
await this.ensureApiServerSpacetimeToken();
|
||||
|
||||
const mergedEnv = {
|
||||
...this.baseEnv,
|
||||
GENARRATIVE_API_HOST: this.options.apiHost,
|
||||
@@ -1413,6 +1434,75 @@ async function isHttpReady(url, timeoutMs = 1000) {
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchSpacetimeIdentity(url) {
|
||||
let response;
|
||||
try {
|
||||
response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`SpacetimeDB identity 请求失败: ${url}; ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
const text = await response.text();
|
||||
if (!response.ok) {
|
||||
throw new Error(`SpacetimeDB identity HTTP ${response.status}: ${trimPreview(text)}`);
|
||||
}
|
||||
|
||||
let payload;
|
||||
try {
|
||||
payload = JSON.parse(text);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`SpacetimeDB identity 响应不是合法 JSON: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
const identity =
|
||||
payload.identity ?? payload.Identity ?? payload.identity_hex ?? payload.identityHex;
|
||||
const token = payload.token ?? payload.Token;
|
||||
if (typeof identity !== 'string' || typeof token !== 'string') {
|
||||
throw new Error(`SpacetimeDB identity 响应缺少 identity/token: ${trimPreview(text)}`);
|
||||
}
|
||||
|
||||
return {identity, token};
|
||||
}
|
||||
|
||||
function shouldTrustExistingSpacetimeToken(existingToken, serverUrl) {
|
||||
const shellToken = String(process.env.GENARRATIVE_SPACETIME_TOKEN ?? '').trim();
|
||||
if (shellToken && shellToken === existingToken) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !isLoopbackSpacetimeServer(serverUrl);
|
||||
}
|
||||
|
||||
function isLoopbackSpacetimeServer(serverUrl) {
|
||||
try {
|
||||
const url = new URL(serverUrl);
|
||||
return ['127.0.0.1', 'localhost', '::1'].includes(url.hostname);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function trimPreview(text, maxLength = 300) {
|
||||
const normalized = String(text ?? '').replace(/\s+/gu, ' ').trim();
|
||||
return normalized.length > maxLength
|
||||
? `${normalized.slice(0, maxLength)}...`
|
||||
: normalized;
|
||||
}
|
||||
|
||||
function runForeground(command, args, {cwd, env, label}) {
|
||||
return new Promise((resolveRun, rejectRun) => {
|
||||
const child = spawn(command, args, {
|
||||
|
||||
Reference in New Issue
Block a user