release: prepare 0629001 portal updates

This commit is contained in:
john
2026-06-29 22:20:04 +08:00
parent 18ea4f82fd
commit a40e340a41
84 changed files with 17516 additions and 2475 deletions
+91 -29
View File
@@ -32,6 +32,7 @@ import {
PUBLISH_SKILL_NAME,
resolvePublicBaseUrl,
resolveLegacyPublishDir,
resolveGoosedSandboxRoot,
} from './user-publish.mjs';
import {
ensureUserSpaceLayout,
@@ -1196,39 +1197,91 @@ export function createUserAuth(pool, options = {}) {
};
}
const tokenState = normalizeTokenState(tokenStateRaw);
const previous = await getBillingState(agentSessionId);
if (
previous &&
tokenState.accumulatedInputTokens <= Number(previous.lastInputTokens ?? 0) &&
tokenState.accumulatedOutputTokens <= Number(previous.lastOutputTokens ?? 0)
) {
const user = await getUserById(userId);
return {
ok: true,
costCents: 0,
balanceCents: user ? Number(user.balance_cents) : null,
tokensUsed: user ? Number(user.tokens_used ?? 0) : null,
deltaInputTokens: 0,
deltaOutputTokens: 0,
};
}
const config = loadBillingConfig();
let costCents = computeDeltaCostCents(previous, tokenState, config);
const deltaIn = Math.max(
0,
tokenState.accumulatedInputTokens - Number(previous?.lastInputTokens ?? 0),
);
const deltaOut = Math.max(
0,
tokenState.accumulatedOutputTokens - Number(previous?.lastOutputTokens ?? 0),
);
const deltaTokens = deltaIn + deltaOut;
const normalizedRequestId = requestId ? String(requestId).trim() || null : null;
const now = Date.now();
const conn = await pool.getConnection();
try {
await conn.beginTransaction();
if (normalizedRequestId) {
const [existingUsage] = await conn.query(
`SELECT cost_cents FROM h5_usage_records WHERE request_id = ? LIMIT 1`,
[normalizedRequestId],
);
if (existingUsage[0]) {
const [walletRows] = await conn.query(
`SELECT balance_cents, tokens_used FROM h5_user_wallets WHERE user_id = ?`,
[userId],
);
await conn.commit();
return {
ok: true,
costCents: 0,
balanceCents: walletRows[0] ? Number(walletRows[0].balance_cents) : null,
tokensUsed: walletRows[0] ? Number(walletRows[0].tokens_used ?? 0) : null,
deltaInputTokens: 0,
deltaOutputTokens: 0,
};
}
}
// Placeholder insert so concurrent billers serialize on the same session row.
await conn.query(
`INSERT INTO h5_session_billing_state
(agent_session_id, user_id, last_accumulated_cost, last_input_tokens, last_output_tokens, updated_at)
VALUES (?, ?, NULL, 0, 0, ?)
ON DUPLICATE KEY UPDATE agent_session_id = agent_session_id`,
[agentSessionId, userId, now],
);
const [stateRows] = await conn.query(
`SELECT last_accumulated_cost, last_input_tokens, last_output_tokens
FROM h5_session_billing_state
WHERE agent_session_id = ?
FOR UPDATE`,
[agentSessionId],
);
const stateRow = stateRows[0];
const previous = stateRow
? {
lastAccumulatedCost: stateRow.last_accumulated_cost,
lastInputTokens: Number(stateRow.last_input_tokens ?? 0),
lastOutputTokens: Number(stateRow.last_output_tokens ?? 0),
}
: null;
if (
previous &&
tokenState.accumulatedInputTokens <= Number(previous.lastInputTokens ?? 0) &&
tokenState.accumulatedOutputTokens <= Number(previous.lastOutputTokens ?? 0)
) {
const [walletRows] = await conn.query(
`SELECT balance_cents, tokens_used FROM h5_user_wallets WHERE user_id = ?`,
[userId],
);
await conn.commit();
return {
ok: true,
costCents: 0,
balanceCents: walletRows[0] ? Number(walletRows[0].balance_cents) : null,
tokensUsed: walletRows[0] ? Number(walletRows[0].tokens_used ?? 0) : null,
deltaInputTokens: 0,
deltaOutputTokens: 0,
};
}
let costCents = computeDeltaCostCents(previous, tokenState, config);
const deltaIn = Math.max(
0,
tokenState.accumulatedInputTokens - Number(previous?.lastInputTokens ?? 0),
);
const deltaOut = Math.max(
0,
tokenState.accumulatedOutputTokens - Number(previous?.lastOutputTokens ?? 0),
);
const deltaTokens = deltaIn + deltaOut;
await conn.query(
`INSERT INTO h5_session_billing_state
(agent_session_id, user_id, last_accumulated_cost, last_input_tokens, last_output_tokens, updated_at)
@@ -1287,7 +1340,16 @@ export function createUserAuth(pool, options = {}) {
`INSERT INTO h5_usage_records
(user_id, agent_session_id, request_id, input_tokens, output_tokens, cost_cents, balance_after_cents, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[userId, agentSessionId, requestId, deltaIn, deltaOut, costCents, nextBalance, now],
[
userId,
agentSessionId,
normalizedRequestId,
deltaIn,
deltaOut,
costCents,
nextBalance,
now,
],
);
await conn.query(
@@ -1668,7 +1730,7 @@ export function createUserAuth(pool, options = {}) {
// paths instead. Unset (e.g. local dev, co-located native goosed) => fall back to
// the portal's own paths, so behavior is unchanged.
serverPath: resolveSandboxMcpServerPath(process.env.GOOSED_MCP_SERVER_PATH),
sandboxRoot: layout.publishDir,
sandboxRoot: resolveGoosedSandboxRoot(h5Root, user),
userId: user.id,
nodeExecPath: process.env.GOOSED_MCP_NODE_PATH,
};