orionfold-relay 0.22.0 → 0.23.0
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 +3 -3
- package/dist/cli.js +153 -51
- package/drizzle.config.ts +2 -2
- package/package.json +1 -1
- package/src/app/api/license/route.ts +1 -1
- package/src/app/api/telemetry/route.ts +6 -5
- package/src/app/packs/page.tsx +254 -39
- package/src/components/notifications/inbox-list.tsx +41 -1
- package/src/components/notifications/unread-badge.tsx +28 -2
- package/src/components/shell/telemetry-rail.tsx +30 -9
- package/src/components/shell/telemetry-types.ts +6 -2
- package/src/instrumentation-node.ts +1 -1
- package/src/lib/agents/claude-agent.ts +20 -0
- package/src/lib/agents/runtime/anthropic-direct.ts +79 -11
- package/src/lib/agents/runtime/claude.ts +12 -8
- package/src/lib/agents/runtime/model-preference.ts +54 -0
- package/src/lib/agents/runtime/openai-direct.ts +7 -2
- package/src/lib/apps/registry.ts +1 -1
- package/src/lib/chat/ollama-engine.ts +51 -1
- package/src/lib/packs/format.ts +38 -2
- package/src/lib/packs/templates/relay-agency/pack.yaml +1 -0
- package/src/lib/packs/templates/relay-agency-pro/pack.yaml +11 -2
- package/src/lib/plugins/examples/echo-server/plugin.yaml +1 -1
- package/src/lib/plugins/examples/finance-pack/plugin.yaml +1 -1
- package/src/lib/plugins/examples/reading-radar/plugin.yaml +1 -1
- package/src/lib/plugins/registry.ts +1 -1
- package/src/lib/plugins/sdk/types.ts +1 -1
- package/src/lib/settings/budget-guardrails.ts +29 -0
- package/src/lib/usage/pricing.ts +7 -0
- package/src/lib/utils/migrate-to-ainative.ts +82 -41
package/README.md
CHANGED
|
@@ -104,7 +104,7 @@ The governance is *in* the workflow, not bolted on. A blueprint is a fixed step
|
|
|
104
104
|
## Why it stays trustworthy
|
|
105
105
|
|
|
106
106
|
- **Local-first** — SQLite database, no cloud dependency, `npx orionfold-relay` and go
|
|
107
|
-
- **
|
|
107
|
+
- **Relay never sends your data to Orionfold** — no telemetry, no update checks, no license server; the complete outbound-network inventory is documented and code-linked in [docs/trust/data-flow.md](docs/trust/data-flow.md)
|
|
108
108
|
- **Your rules, enforced** — tool permissions, inbox approvals, and audit trails for every agent action
|
|
109
109
|
- **Your AI team** — 21 specialist profiles ready to deploy, each with instructions, tool policies, and runtime tuning
|
|
110
110
|
- **Know what you spend** — usage metering, budgets, and per-provider/per-model spend visibility on governed runs
|
|
@@ -134,8 +134,8 @@ relay license remove <license-id> # forget a license
|
|
|
134
134
|
|
|
135
135
|
- **Verification is 100% offline** — an Ed25519 signature check against keys embedded in
|
|
136
136
|
this repo ([`src/lib/licensing/verify.ts`](src/lib/licensing/verify.ts)). Relay never
|
|
137
|
-
|
|
138
|
-
air-gapped.
|
|
137
|
+
sends your data to Orionfold: no activation server, no telemetry, no network call of
|
|
138
|
+
any kind. Works air-gapped.
|
|
139
139
|
- **Your packs are yours forever. Renewal gets you the year's new and updated packs +
|
|
140
140
|
priority support.** An expired or removed license never re-locks content you already
|
|
141
141
|
installed — it only gates new premium installs and updates.
|
package/dist/cli.js
CHANGED
|
@@ -1186,7 +1186,7 @@ var CURRENT_PLUGIN_API_VERSION, CAPABILITY_VALUES, ORIGIN_VALUES, PrimitivesBund
|
|
|
1186
1186
|
var init_types = __esm({
|
|
1187
1187
|
"src/lib/plugins/sdk/types.ts"() {
|
|
1188
1188
|
"use strict";
|
|
1189
|
-
CURRENT_PLUGIN_API_VERSION = "0.
|
|
1189
|
+
CURRENT_PLUGIN_API_VERSION = "0.23";
|
|
1190
1190
|
CAPABILITY_VALUES = ["fs", "net", "child_process", "env"];
|
|
1191
1191
|
ORIGIN_VALUES = ["ainative-internal", "third-party"];
|
|
1192
1192
|
PrimitivesBundleManifestSchema = z.object({
|
|
@@ -3739,9 +3739,25 @@ var init_format = __esm({
|
|
|
3739
3739
|
/**
|
|
3740
3740
|
* Premium display copy (D6). Offline strings rendered on the locked
|
|
3741
3741
|
* gallery card — the Website still owns actual pricing. Meaningful only
|
|
3742
|
-
* alongside `entitlement`; harmless on a free pack.
|
|
3742
|
+
* alongside `entitlement`; harmless on a free pack. Either a flat string
|
|
3743
|
+
* ("$499/year") or a two-phase offer ({ list, intro?, note? }) so a
|
|
3744
|
+
* founding/introductory price can render alongside the list price.
|
|
3745
|
+
* Render sites consume `packPrice()` — never branch on the raw shape.
|
|
3743
3746
|
*/
|
|
3744
|
-
price: z3.
|
|
3747
|
+
price: z3.union([
|
|
3748
|
+
z3.string().min(1),
|
|
3749
|
+
z3.object({
|
|
3750
|
+
list: z3.string().min(1),
|
|
3751
|
+
intro: z3.string().min(1).optional(),
|
|
3752
|
+
note: z3.string().min(1).optional()
|
|
3753
|
+
}).strict()
|
|
3754
|
+
]).optional(),
|
|
3755
|
+
/**
|
|
3756
|
+
* Card identity token — a lucide icon name rendered on the gallery card
|
|
3757
|
+
* (e.g. "briefcase"). Unknown tokens fall back to the default glyph;
|
|
3758
|
+
* never a remote asset.
|
|
3759
|
+
*/
|
|
3760
|
+
icon: z3.string().min(1).optional(),
|
|
3745
3761
|
/** Get-license CTA target on the locked card. */
|
|
3746
3762
|
purchaseUrl: z3.url().optional(),
|
|
3747
3763
|
/**
|
|
@@ -6910,6 +6926,34 @@ var init_output_scanner = __esm({
|
|
|
6910
6926
|
}
|
|
6911
6927
|
});
|
|
6912
6928
|
|
|
6929
|
+
// src/lib/agents/runtime/model-preference.ts
|
|
6930
|
+
var model_preference_exports = {};
|
|
6931
|
+
__export(model_preference_exports, {
|
|
6932
|
+
resolvePreferredModel: () => resolvePreferredModel
|
|
6933
|
+
});
|
|
6934
|
+
async function resolvePreferredModel(runtimeId, options) {
|
|
6935
|
+
if (options?.pinnedModelId) {
|
|
6936
|
+
return { modelId: options.pinnedModelId, source: "pin" };
|
|
6937
|
+
}
|
|
6938
|
+
const models = getRuntimeCatalogEntry(runtimeId).models;
|
|
6939
|
+
const preference = await getModelPreference();
|
|
6940
|
+
const tierModel = preference === "balanced" ? models.tiers?.balanced : preference === "cost" ? models.tiers?.fast : preference === "quality" ? models.tiers?.quality : void 0;
|
|
6941
|
+
if (tierModel) {
|
|
6942
|
+
return { modelId: tierModel, source: "preference" };
|
|
6943
|
+
}
|
|
6944
|
+
return {
|
|
6945
|
+
modelId: models.tiers?.quality ?? models.default,
|
|
6946
|
+
source: "default"
|
|
6947
|
+
};
|
|
6948
|
+
}
|
|
6949
|
+
var init_model_preference = __esm({
|
|
6950
|
+
"src/lib/agents/runtime/model-preference.ts"() {
|
|
6951
|
+
"use strict";
|
|
6952
|
+
init_catalog2();
|
|
6953
|
+
init_helpers();
|
|
6954
|
+
}
|
|
6955
|
+
});
|
|
6956
|
+
|
|
6913
6957
|
// src/lib/agents/runtime/claude-sdk.ts
|
|
6914
6958
|
function buildClaudeSdkEnv(authEnv) {
|
|
6915
6959
|
const { CLAUDECODE, ANTHROPIC_API_KEY, ...cleanEnv } = process.env;
|
|
@@ -7679,6 +7723,9 @@ async function deriveUsageCostMicros(input) {
|
|
|
7679
7723
|
if (!input.modelId) {
|
|
7680
7724
|
return { costMicros: null, pricingVersion: null };
|
|
7681
7725
|
}
|
|
7726
|
+
if (input.providerId === "ollama") {
|
|
7727
|
+
return { costMicros: 0, pricingVersion: "local-free" };
|
|
7728
|
+
}
|
|
7682
7729
|
if (input.providerId !== "anthropic" && input.providerId !== "openai") {
|
|
7683
7730
|
return { costMicros: null, pricingVersion: null };
|
|
7684
7731
|
}
|
|
@@ -12898,7 +12945,7 @@ var init_registry6 = __esm({
|
|
|
12898
12945
|
init_registry5();
|
|
12899
12946
|
init_installer();
|
|
12900
12947
|
init_schedule_spec();
|
|
12901
|
-
SUPPORTED_API_VERSIONS = /* @__PURE__ */ new Set([CURRENT_PLUGIN_API_VERSION, "0.
|
|
12948
|
+
SUPPORTED_API_VERSIONS = /* @__PURE__ */ new Set([CURRENT_PLUGIN_API_VERSION, "0.22"]);
|
|
12902
12949
|
pluginCache = null;
|
|
12903
12950
|
lastLoadedPluginIds = /* @__PURE__ */ new Set();
|
|
12904
12951
|
PluginTableSchema = z16.object({
|
|
@@ -18074,13 +18121,17 @@ ${learnedCtx}
|
|
|
18074
18121
|
You are operating inside a git worktree (branch: ${ws.gitBranch ?? "unknown"}). All file operations MUST use paths relative to the working directory: ${cwd}. Do NOT navigate to or create files in the main repository directory.` : "";
|
|
18075
18122
|
const systemInstructions = [worktreeNote, profileInstructions, learnedCtxBlock, docContext, tableContext, outputInstructions].filter(Boolean).join("\n\n");
|
|
18076
18123
|
const maxTurns = profile?.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
18124
|
+
const { modelId } = await resolvePreferredModel("claude-code", {
|
|
18125
|
+
pinnedModelId: profile?.capabilityOverrides?.["claude-code"]?.modelId
|
|
18126
|
+
});
|
|
18077
18127
|
return {
|
|
18078
18128
|
userPrompt: basePrompt,
|
|
18079
18129
|
systemInstructions,
|
|
18080
18130
|
cwd,
|
|
18081
18131
|
payload,
|
|
18082
18132
|
maxTurns,
|
|
18083
|
-
canUseToolPolicy: payload?.canUseToolPolicy
|
|
18133
|
+
canUseToolPolicy: payload?.canUseToolPolicy,
|
|
18134
|
+
modelId
|
|
18084
18135
|
};
|
|
18085
18136
|
}
|
|
18086
18137
|
async function executeClaudeTask(taskId) {
|
|
@@ -18126,6 +18177,10 @@ async function executeClaudeTask(taskId) {
|
|
|
18126
18177
|
prompt: ctx.userPrompt,
|
|
18127
18178
|
options: {
|
|
18128
18179
|
abortController,
|
|
18180
|
+
// Explicit model: profile pin > onboarding preference > quality
|
|
18181
|
+
// default. Omitting this let the SDK pick ITS default (Opus) and
|
|
18182
|
+
// silently bill the wrong tier.
|
|
18183
|
+
model: ctx.modelId,
|
|
18129
18184
|
includePartialMessages: true,
|
|
18130
18185
|
cwd: ctx.cwd,
|
|
18131
18186
|
env: buildClaudeSdkEnv(authEnv),
|
|
@@ -18248,6 +18303,9 @@ async function resumeClaudeTask(taskId) {
|
|
|
18248
18303
|
options: {
|
|
18249
18304
|
resume: task.sessionId,
|
|
18250
18305
|
abortController,
|
|
18306
|
+
// Same model resolution as the original run — a resume must not
|
|
18307
|
+
// silently hop tiers (profile pin > preference > quality default).
|
|
18308
|
+
model: ctx.modelId,
|
|
18251
18309
|
includePartialMessages: true,
|
|
18252
18310
|
cwd: ctx.cwd,
|
|
18253
18311
|
env: buildClaudeSdkEnv(authEnv),
|
|
@@ -18366,6 +18424,7 @@ var init_claude_agent = __esm({
|
|
|
18366
18424
|
init_context_builder2();
|
|
18367
18425
|
init_output_scanner();
|
|
18368
18426
|
init_registry2();
|
|
18427
|
+
init_model_preference();
|
|
18369
18428
|
init_compatibility();
|
|
18370
18429
|
init_claude_sdk();
|
|
18371
18430
|
init_types2();
|
|
@@ -18384,9 +18443,8 @@ var init_claude_agent = __esm({
|
|
|
18384
18443
|
// src/lib/agents/runtime/claude.ts
|
|
18385
18444
|
import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
|
|
18386
18445
|
import { eq as eq34 } from "drizzle-orm";
|
|
18387
|
-
function claudeCodeModelAlias() {
|
|
18388
|
-
|
|
18389
|
-
return models.tiers?.quality ?? models.default;
|
|
18446
|
+
async function claudeCodeModelAlias() {
|
|
18447
|
+
return (await resolvePreferredModel("claude-code")).modelId;
|
|
18390
18448
|
}
|
|
18391
18449
|
function buildTaskAssistSystemPrompt(profileIds) {
|
|
18392
18450
|
const profileList = profileIds.length > 0 ? `Available agent profiles: ${profileIds.join(", ")}
|
|
@@ -18480,7 +18538,7 @@ Provide a brief analysis (2-3 paragraphs max). Include specific terminology rele
|
|
|
18480
18538
|
prompt,
|
|
18481
18539
|
options: {
|
|
18482
18540
|
abortController,
|
|
18483
|
-
model: claudeCodeModelAlias(),
|
|
18541
|
+
model: await claudeCodeModelAlias(),
|
|
18484
18542
|
includePartialMessages: true,
|
|
18485
18543
|
env: buildClaudeSdkEnv(authEnv),
|
|
18486
18544
|
allowedTools: []
|
|
@@ -18621,7 +18679,7 @@ async function runMetaCompletion(input) {
|
|
|
18621
18679
|
prompt: input.prompt,
|
|
18622
18680
|
options: {
|
|
18623
18681
|
abortController,
|
|
18624
|
-
model: claudeCodeModelAlias(),
|
|
18682
|
+
model: await claudeCodeModelAlias(),
|
|
18625
18683
|
includePartialMessages: true,
|
|
18626
18684
|
cwd: getLaunchCwd(),
|
|
18627
18685
|
env: buildClaudeSdkEnv(authEnv),
|
|
@@ -18801,7 +18859,7 @@ ${userMessage}`;
|
|
|
18801
18859
|
prompt,
|
|
18802
18860
|
options: {
|
|
18803
18861
|
abortController,
|
|
18804
|
-
model: claudeCodeModelAlias(),
|
|
18862
|
+
model: await claudeCodeModelAlias(),
|
|
18805
18863
|
includePartialMessages: true,
|
|
18806
18864
|
cwd: getLaunchCwd(),
|
|
18807
18865
|
env: buildClaudeSdkEnv(authEnv),
|
|
@@ -18876,7 +18934,7 @@ ${userMessage}`;
|
|
|
18876
18934
|
prompt,
|
|
18877
18935
|
options: {
|
|
18878
18936
|
abortController,
|
|
18879
|
-
model: claudeCodeModelAlias(),
|
|
18937
|
+
model: await claudeCodeModelAlias(),
|
|
18880
18938
|
includePartialMessages: true,
|
|
18881
18939
|
cwd: getLaunchCwd(),
|
|
18882
18940
|
env: buildClaudeSdkEnv(authEnv),
|
|
@@ -18941,7 +18999,7 @@ async function testClaudeConnection() {
|
|
|
18941
18999
|
prompt: "Reply with exactly: OK",
|
|
18942
19000
|
options: {
|
|
18943
19001
|
abortController,
|
|
18944
|
-
model: claudeCodeModelAlias(),
|
|
19002
|
+
model: await claudeCodeModelAlias(),
|
|
18945
19003
|
maxTurns: 1,
|
|
18946
19004
|
includePartialMessages: false,
|
|
18947
19005
|
cwd: getLaunchCwd(),
|
|
@@ -18988,6 +19046,7 @@ var init_claude = __esm({
|
|
|
18988
19046
|
init_compatibility();
|
|
18989
19047
|
init_claude_agent();
|
|
18990
19048
|
init_catalog2();
|
|
19049
|
+
init_model_preference();
|
|
18991
19050
|
init_claude_sdk();
|
|
18992
19051
|
init_workspace_context();
|
|
18993
19052
|
init_helpers();
|
|
@@ -20703,6 +20762,24 @@ async function withAnthropicDirectMcpServers(profileServers, browserServers, ext
|
|
|
20703
20762
|
relay: relayServer
|
|
20704
20763
|
};
|
|
20705
20764
|
}
|
|
20765
|
+
function mcpServersToAnthropicConnectors(mergedServers) {
|
|
20766
|
+
const connectors = [];
|
|
20767
|
+
for (const [name, config] of Object.entries(mergedServers)) {
|
|
20768
|
+
if (name === "relay") continue;
|
|
20769
|
+
const cfg = config;
|
|
20770
|
+
const url = cfg.url ?? cfg.server_url;
|
|
20771
|
+
if (typeof url !== "string" || url.length === 0) {
|
|
20772
|
+
continue;
|
|
20773
|
+
}
|
|
20774
|
+
const connector = { type: "url", url, name };
|
|
20775
|
+
for (const [key, value] of Object.entries(cfg)) {
|
|
20776
|
+
if (["url", "server_url", "command", "args", "env"].includes(key)) continue;
|
|
20777
|
+
if (typeof value === "string") connector[key] = value;
|
|
20778
|
+
}
|
|
20779
|
+
connectors.push(connector);
|
|
20780
|
+
}
|
|
20781
|
+
return connectors;
|
|
20782
|
+
}
|
|
20706
20783
|
async function getAnthropicSDK() {
|
|
20707
20784
|
if (!_sdk) {
|
|
20708
20785
|
_sdk = await import("@anthropic-ai/sdk");
|
|
@@ -20769,7 +20846,7 @@ async function callAnthropicModel(client, systemPrompt, messages, tools, signal,
|
|
|
20769
20846
|
budget_tokens: options.extendedThinking.budgetTokens ?? 1e4
|
|
20770
20847
|
};
|
|
20771
20848
|
}
|
|
20772
|
-
if (options.mcpServers &&
|
|
20849
|
+
if (options.mcpServers && options.mcpServers.length > 0) {
|
|
20773
20850
|
params.mcp_servers = options.mcpServers;
|
|
20774
20851
|
}
|
|
20775
20852
|
const stream = client.messages.stream(params, { signal });
|
|
@@ -20874,6 +20951,7 @@ ${outputInstructions}`;
|
|
|
20874
20951
|
pluginServers,
|
|
20875
20952
|
task.projectId
|
|
20876
20953
|
);
|
|
20954
|
+
const mcpConnectors = mcpServersToAnthropicConnectors(mergedMcpServers);
|
|
20877
20955
|
let initialMessages;
|
|
20878
20956
|
if (isResume) {
|
|
20879
20957
|
const snapshot = await loadSessionSnapshot(taskId);
|
|
@@ -20882,7 +20960,8 @@ ${outputInstructions}`;
|
|
|
20882
20960
|
initialMessages = [{ role: "user", content: ctx.userPrompt }];
|
|
20883
20961
|
}
|
|
20884
20962
|
const { getSetting: getSetting2 } = await Promise.resolve().then(() => (init_helpers(), helpers_exports));
|
|
20885
|
-
const
|
|
20963
|
+
const { resolvePreferredModel: resolvePreferredModel2 } = await Promise.resolve().then(() => (init_model_preference(), model_preference_exports));
|
|
20964
|
+
const modelId = await getSetting2("anthropic_direct_model") ?? (await resolvePreferredModel2("anthropic-direct")).modelId;
|
|
20886
20965
|
const maxTurns = ctx.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
20887
20966
|
await db.insert(agentLogs).values({
|
|
20888
20967
|
id: crypto.randomUUID(),
|
|
@@ -20921,7 +21000,7 @@ ${outputInstructions}`;
|
|
|
20921
21000
|
enableCaching: true,
|
|
20922
21001
|
profileInstructions: profile?.skillMd,
|
|
20923
21002
|
extendedThinking: capOverrides?.extendedThinking,
|
|
20924
|
-
mcpServers:
|
|
21003
|
+
mcpServers: mcpConnectors
|
|
20925
21004
|
}
|
|
20926
21005
|
);
|
|
20927
21006
|
await saveSessionSnapshot(taskId, agentProfileId, [
|
|
@@ -21353,7 +21432,8 @@ ${outputInstructions}`;
|
|
|
21353
21432
|
);
|
|
21354
21433
|
const pluginMcpTools = mcpServersToOpenAiTools(mergedMcpServers);
|
|
21355
21434
|
const { getSetting: getSetting2 } = await Promise.resolve().then(() => (init_helpers(), helpers_exports));
|
|
21356
|
-
const
|
|
21435
|
+
const { resolvePreferredModel: resolvePreferredModel2 } = await Promise.resolve().then(() => (init_model_preference(), model_preference_exports));
|
|
21436
|
+
const modelId = await getSetting2("openai_direct_model") ?? (await resolvePreferredModel2("openai-direct")).modelId;
|
|
21357
21437
|
const maxTurns = ctx.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
21358
21438
|
let previousResponseId = null;
|
|
21359
21439
|
if (isResume) {
|
|
@@ -22193,6 +22273,17 @@ async function getUsageAggregates(policy, runtimeStates, now = /* @__PURE__ */ n
|
|
|
22193
22273
|
runtimes[runtimeId].daily.totalTokens += row.totalTokens ?? 0;
|
|
22194
22274
|
}
|
|
22195
22275
|
});
|
|
22276
|
+
const metered = {
|
|
22277
|
+
daily: { costMicros: 0, totalTokens: 0 },
|
|
22278
|
+
monthly: { costMicros: 0, totalTokens: 0 }
|
|
22279
|
+
};
|
|
22280
|
+
for (const runtimeId of SUPPORTED_AGENT_RUNTIMES) {
|
|
22281
|
+
metered.daily.costMicros += runtimes[runtimeId].daily.costMicros;
|
|
22282
|
+
metered.daily.totalTokens += runtimes[runtimeId].daily.totalTokens;
|
|
22283
|
+
metered.monthly.costMicros += runtimes[runtimeId].monthly.costMicros;
|
|
22284
|
+
metered.monthly.totalTokens += runtimes[runtimeId].monthly.totalTokens;
|
|
22285
|
+
}
|
|
22286
|
+
let planPricedMonthlyMicros = null;
|
|
22196
22287
|
if (runtimeStates["claude-code"].billingMode === "subscription") {
|
|
22197
22288
|
const planPriceUsd = await getClaudeOAuthPlanPrice(
|
|
22198
22289
|
policy.runtimes["claude-code"].claudeOAuthPlan
|
|
@@ -22201,6 +22292,7 @@ async function getUsageAggregates(policy, runtimeStates, now = /* @__PURE__ */ n
|
|
|
22201
22292
|
const dailyMicros = Math.round(monthlyMicros / daysInMonth(now));
|
|
22202
22293
|
runtimes["claude-code"].monthly.costMicros = monthlyMicros;
|
|
22203
22294
|
runtimes["claude-code"].daily.costMicros = dailyMicros;
|
|
22295
|
+
planPricedMonthlyMicros = monthlyMicros;
|
|
22204
22296
|
}
|
|
22205
22297
|
const overall = {
|
|
22206
22298
|
daily: { costMicros: 0, totalTokens: 0 },
|
|
@@ -22218,6 +22310,8 @@ async function getUsageAggregates(policy, runtimeStates, now = /* @__PURE__ */ n
|
|
|
22218
22310
|
return {
|
|
22219
22311
|
overall,
|
|
22220
22312
|
runtimes,
|
|
22313
|
+
metered,
|
|
22314
|
+
planPricedMonthlyMicros,
|
|
22221
22315
|
...getBudgetWindowBounds(now)
|
|
22222
22316
|
};
|
|
22223
22317
|
}
|
|
@@ -25681,8 +25775,8 @@ import { execFileSync as execFileSync3 } from "child_process";
|
|
|
25681
25775
|
import yaml12 from "js-yaml";
|
|
25682
25776
|
import semver from "semver";
|
|
25683
25777
|
function relayCoreVersion() {
|
|
25684
|
-
if (semver.valid("0.
|
|
25685
|
-
return "0.
|
|
25778
|
+
if (semver.valid("0.23.0")) {
|
|
25779
|
+
return "0.23.0";
|
|
25686
25780
|
}
|
|
25687
25781
|
try {
|
|
25688
25782
|
const root = getAppRoot(import.meta.dirname, 3);
|
|
@@ -26925,6 +27019,10 @@ function hasSqliteHeader(path24) {
|
|
|
26925
27019
|
return false;
|
|
26926
27020
|
}
|
|
26927
27021
|
}
|
|
27022
|
+
var MIGRATION_CHAIN = [
|
|
27023
|
+
{ fromDir: ".stagent", toDir: ".ainative", fromDb: "stagent.db", toDb: "ainative.db" },
|
|
27024
|
+
{ fromDir: ".ainative", toDir: ".relay", fromDb: "ainative.db", toDb: "relay.db" }
|
|
27025
|
+
];
|
|
26928
27026
|
async function migrateLegacyData(options = {}) {
|
|
26929
27027
|
const home = options.home ?? homedir2();
|
|
26930
27028
|
const gitDir = options.gitDir ?? join6(process.cwd(), ".git");
|
|
@@ -26937,46 +27035,50 @@ async function migrateLegacyData(options = {}) {
|
|
|
26937
27035
|
keychainMigrated: false,
|
|
26938
27036
|
errors: []
|
|
26939
27037
|
};
|
|
26940
|
-
const
|
|
26941
|
-
const
|
|
26942
|
-
|
|
26943
|
-
|
|
26944
|
-
|
|
26945
|
-
|
|
26946
|
-
|
|
26947
|
-
|
|
26948
|
-
|
|
26949
|
-
|
|
26950
|
-
|
|
26951
|
-
|
|
26952
|
-
|
|
26953
|
-
|
|
26954
|
-
|
|
26955
|
-
|
|
26956
|
-
|
|
27038
|
+
const finalDir = join6(home, MIGRATION_CHAIN[MIGRATION_CHAIN.length - 1].toDir);
|
|
27039
|
+
const finalDbName = MIGRATION_CHAIN[MIGRATION_CHAIN.length - 1].toDb;
|
|
27040
|
+
for (const hop of MIGRATION_CHAIN) {
|
|
27041
|
+
const oldDir = join6(home, hop.fromDir);
|
|
27042
|
+
const newDir = join6(home, hop.toDir);
|
|
27043
|
+
if (existsSync4(oldDir) && !existsSync4(newDir)) {
|
|
27044
|
+
try {
|
|
27045
|
+
renameSync(oldDir, newDir);
|
|
27046
|
+
report.dirMigrated = true;
|
|
27047
|
+
log(`renamed ${oldDir} -> ${newDir}`);
|
|
27048
|
+
} catch (err2) {
|
|
27049
|
+
const e = err2;
|
|
27050
|
+
if (e.code === "EXDEV") {
|
|
27051
|
+
try {
|
|
27052
|
+
cpSync(oldDir, newDir, { recursive: true });
|
|
27053
|
+
rmSync2(oldDir, { recursive: true, force: true });
|
|
27054
|
+
report.dirMigrated = true;
|
|
27055
|
+
log(`copied ${oldDir} -> ${newDir} (cross-device fallback)`);
|
|
27056
|
+
} catch (copyErr) {
|
|
27057
|
+
report.errors.push(`dir copy failed: ${String(copyErr)}`);
|
|
27058
|
+
return report;
|
|
27059
|
+
}
|
|
27060
|
+
} else {
|
|
27061
|
+
report.errors.push(`dir rename failed: ${String(err2)}`);
|
|
26957
27062
|
return report;
|
|
26958
27063
|
}
|
|
26959
|
-
} else {
|
|
26960
|
-
report.errors.push(`dir rename failed: ${String(err2)}`);
|
|
26961
|
-
return report;
|
|
26962
27064
|
}
|
|
26963
27065
|
}
|
|
26964
|
-
|
|
26965
|
-
|
|
26966
|
-
|
|
26967
|
-
|
|
26968
|
-
|
|
26969
|
-
|
|
26970
|
-
|
|
26971
|
-
|
|
26972
|
-
|
|
26973
|
-
|
|
26974
|
-
|
|
27066
|
+
if (existsSync4(newDir)) {
|
|
27067
|
+
for (const suffix of ["", "-shm", "-wal"]) {
|
|
27068
|
+
const oldName = join6(newDir, `${hop.fromDb}${suffix}`);
|
|
27069
|
+
const newName = join6(newDir, `${hop.toDb}${suffix}`);
|
|
27070
|
+
if (existsSync4(oldName) && !existsSync4(newName)) {
|
|
27071
|
+
try {
|
|
27072
|
+
renameSync(oldName, newName);
|
|
27073
|
+
report.dbFilesRenamed++;
|
|
27074
|
+
} catch (err2) {
|
|
27075
|
+
report.errors.push(`db file rename failed (${suffix}): ${String(err2)}`);
|
|
27076
|
+
}
|
|
26975
27077
|
}
|
|
26976
27078
|
}
|
|
26977
27079
|
}
|
|
26978
27080
|
}
|
|
26979
|
-
const dbPath4 = join6(
|
|
27081
|
+
const dbPath4 = join6(finalDir, finalDbName);
|
|
26980
27082
|
if (existsSync4(dbPath4) && !hasSqliteHeader(dbPath4)) {
|
|
26981
27083
|
log(`skipping SQL migration \u2014 ${dbPath4} exists but lacks SQLite header`);
|
|
26982
27084
|
}
|
package/drizzle.config.ts
CHANGED
|
@@ -2,13 +2,13 @@ import { defineConfig } from "drizzle-kit";
|
|
|
2
2
|
import { homedir } from "os";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
|
|
5
|
-
const dataDir = process.env.
|
|
5
|
+
const dataDir = process.env.RELAY_DATA_DIR || join(homedir(), ".relay");
|
|
6
6
|
|
|
7
7
|
export default defineConfig({
|
|
8
8
|
schema: "./src/lib/db/schema.ts",
|
|
9
9
|
out: "./src/lib/db/migrations",
|
|
10
10
|
dialect: "sqlite",
|
|
11
11
|
dbCredentials: {
|
|
12
|
-
url: join(dataDir, "
|
|
12
|
+
url: join(dataDir, "relay.db"),
|
|
13
13
|
},
|
|
14
14
|
});
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@ import type { SignedLicense } from "@/lib/licensing/verify";
|
|
|
14
14
|
* paste/upload activation path for UI-first users: the browser reads the
|
|
15
15
|
* fulfilment file client-side and ships the `{ payload, signature }` envelope
|
|
16
16
|
* as JSON. Verification is offline Ed25519 inside saveLicense; nothing here
|
|
17
|
-
*
|
|
17
|
+
* sends user data to Orionfold.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
const BodySchema = z.object({
|
|
@@ -175,9 +175,6 @@ export async function GET() {
|
|
|
175
175
|
getFailuresByDay(7),
|
|
176
176
|
]);
|
|
177
177
|
|
|
178
|
-
const overallDaily = budget.statuses.find(
|
|
179
|
-
(s) => s.scopeId === "overall" && s.window === "daily",
|
|
180
|
-
);
|
|
181
178
|
const overallMonthly = budget.statuses.find(
|
|
182
179
|
(s) => s.scopeId === "overall" && s.window === "monthly",
|
|
183
180
|
);
|
|
@@ -196,8 +193,12 @@ export async function GET() {
|
|
|
196
193
|
activeProjects: activeProjects?.count ?? 0,
|
|
197
194
|
activeWorkflows: activeWorkflows?.count ?? 0,
|
|
198
195
|
reviewPending,
|
|
199
|
-
|
|
200
|
-
|
|
196
|
+
// Metered ledger sums only — the guardrail statuses' plan-priced budget
|
|
197
|
+
// basis must never render as "cost" (fix-dashboard-budget-vs-cost-labeling).
|
|
198
|
+
costTodayMicros: budget.meteredSpend.dailyMicros,
|
|
199
|
+
costToDateMicros: budget.meteredSpend.monthlyMicros,
|
|
200
|
+
budgetMonthlyCapMicros: overallMonthly?.limitValue ?? null,
|
|
201
|
+
planPricedMonthlyMicros: budget.planPricedMonthlyMicros,
|
|
201
202
|
runtimeLabel,
|
|
202
203
|
providerId,
|
|
203
204
|
runtimeSdkVersion,
|