orionfold-relay 0.21.0 → 0.22.1
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/dist/cli.js +237 -58
- package/package.json +1 -1
- package/src/app/api/telemetry/route.ts +6 -5
- package/src/app/packs/page.tsx +19 -5
- package/src/components/shell/telemetry-rail.tsx +30 -9
- package/src/components/shell/telemetry-types.ts +6 -2
- package/src/lib/agents/claude-agent.ts +20 -0
- package/src/lib/agents/runtime/anthropic-direct.ts +7 -2
- 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/chat/ollama-engine.ts +51 -1
- package/src/lib/licensing/cli.ts +59 -0
- package/src/lib/licensing/recap.ts +119 -0
- package/src/lib/packs/format.ts +7 -0
- package/src/lib/packs/templates/relay-agency-pro/pack.yaml +14 -1
- package/src/lib/packs/update.ts +20 -0
- 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/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.22";
|
|
1190
1190
|
CAPABILITY_VALUES = ["fs", "net", "child_process", "env"];
|
|
1191
1191
|
ORIGIN_VALUES = ["ainative-internal", "third-party"];
|
|
1192
1192
|
PrimitivesBundleManifestSchema = z.object({
|
|
@@ -1232,16 +1232,16 @@ function computeHash(content) {
|
|
|
1232
1232
|
function safePreview(content) {
|
|
1233
1233
|
return content.slice(0, MAX_PREVIEW_CHARS).trim();
|
|
1234
1234
|
}
|
|
1235
|
-
function safeStat(
|
|
1235
|
+
function safeStat(path24) {
|
|
1236
1236
|
try {
|
|
1237
|
-
return statSync2(
|
|
1237
|
+
return statSync2(path24);
|
|
1238
1238
|
} catch {
|
|
1239
1239
|
return null;
|
|
1240
1240
|
}
|
|
1241
1241
|
}
|
|
1242
|
-
function safeReadFile(
|
|
1242
|
+
function safeReadFile(path24) {
|
|
1243
1243
|
try {
|
|
1244
|
-
return readFileSync4(
|
|
1244
|
+
return readFileSync4(path24, "utf-8");
|
|
1245
1245
|
} catch {
|
|
1246
1246
|
return null;
|
|
1247
1247
|
}
|
|
@@ -3744,6 +3744,13 @@ var init_format = __esm({
|
|
|
3744
3744
|
price: z3.string().min(1).optional(),
|
|
3745
3745
|
/** Get-license CTA target on the locked card. */
|
|
3746
3746
|
purchaseUrl: z3.url().optional(),
|
|
3747
|
+
/**
|
|
3748
|
+
* Per-version customer-voice recap, version → one line. The single source
|
|
3749
|
+
* for every renewal value-recap surface (`license status`, the 402 update
|
|
3750
|
+
* refusal, the /packs update card, the Website renewal email). Optional —
|
|
3751
|
+
* but paid packs should carry it, or their renewal case argues generically.
|
|
3752
|
+
*/
|
|
3753
|
+
changelog: z3.record(z3.string(), z3.string().min(1)).optional(),
|
|
3747
3754
|
/** Customer slugs seeded via ensureCustomer at install. */
|
|
3748
3755
|
customers: z3.array(z3.string()).default([])
|
|
3749
3756
|
}).strict();
|
|
@@ -6903,6 +6910,34 @@ var init_output_scanner = __esm({
|
|
|
6903
6910
|
}
|
|
6904
6911
|
});
|
|
6905
6912
|
|
|
6913
|
+
// src/lib/agents/runtime/model-preference.ts
|
|
6914
|
+
var model_preference_exports = {};
|
|
6915
|
+
__export(model_preference_exports, {
|
|
6916
|
+
resolvePreferredModel: () => resolvePreferredModel
|
|
6917
|
+
});
|
|
6918
|
+
async function resolvePreferredModel(runtimeId, options) {
|
|
6919
|
+
if (options?.pinnedModelId) {
|
|
6920
|
+
return { modelId: options.pinnedModelId, source: "pin" };
|
|
6921
|
+
}
|
|
6922
|
+
const models = getRuntimeCatalogEntry(runtimeId).models;
|
|
6923
|
+
const preference = await getModelPreference();
|
|
6924
|
+
const tierModel = preference === "balanced" ? models.tiers?.balanced : preference === "cost" ? models.tiers?.fast : preference === "quality" ? models.tiers?.quality : void 0;
|
|
6925
|
+
if (tierModel) {
|
|
6926
|
+
return { modelId: tierModel, source: "preference" };
|
|
6927
|
+
}
|
|
6928
|
+
return {
|
|
6929
|
+
modelId: models.tiers?.quality ?? models.default,
|
|
6930
|
+
source: "default"
|
|
6931
|
+
};
|
|
6932
|
+
}
|
|
6933
|
+
var init_model_preference = __esm({
|
|
6934
|
+
"src/lib/agents/runtime/model-preference.ts"() {
|
|
6935
|
+
"use strict";
|
|
6936
|
+
init_catalog2();
|
|
6937
|
+
init_helpers();
|
|
6938
|
+
}
|
|
6939
|
+
});
|
|
6940
|
+
|
|
6906
6941
|
// src/lib/agents/runtime/claude-sdk.ts
|
|
6907
6942
|
function buildClaudeSdkEnv(authEnv) {
|
|
6908
6943
|
const { CLAUDECODE, ANTHROPIC_API_KEY, ...cleanEnv } = process.env;
|
|
@@ -7672,6 +7707,9 @@ async function deriveUsageCostMicros(input) {
|
|
|
7672
7707
|
if (!input.modelId) {
|
|
7673
7708
|
return { costMicros: null, pricingVersion: null };
|
|
7674
7709
|
}
|
|
7710
|
+
if (input.providerId === "ollama") {
|
|
7711
|
+
return { costMicros: 0, pricingVersion: "local-free" };
|
|
7712
|
+
}
|
|
7675
7713
|
if (input.providerId !== "anthropic" && input.providerId !== "openai") {
|
|
7676
7714
|
return { costMicros: null, pricingVersion: null };
|
|
7677
7715
|
}
|
|
@@ -11951,9 +11989,9 @@ function buildPermissionSummary(toolName, input) {
|
|
|
11951
11989
|
}
|
|
11952
11990
|
}
|
|
11953
11991
|
if (toolName === "Read" || toolName === "Write" || toolName === "Edit" || toolName === "read" || toolName === "write" || toolName === "edit") {
|
|
11954
|
-
const
|
|
11955
|
-
if (typeof
|
|
11956
|
-
return truncate(
|
|
11992
|
+
const path24 = input.file_path ?? input.path;
|
|
11993
|
+
if (typeof path24 === "string" && path24.trim().length > 0) {
|
|
11994
|
+
return truncate(path24.trim());
|
|
11957
11995
|
}
|
|
11958
11996
|
}
|
|
11959
11997
|
if (toolName?.startsWith("mcp__")) {
|
|
@@ -11987,9 +12025,9 @@ function getPermissionDetailEntries(toolName, input) {
|
|
|
11987
12025
|
}
|
|
11988
12026
|
}
|
|
11989
12027
|
if (toolName === "Read" || toolName === "Write" || toolName === "Edit" || toolName === "read" || toolName === "write" || toolName === "edit") {
|
|
11990
|
-
const
|
|
11991
|
-
if (typeof
|
|
11992
|
-
return [{ label: "Path", value:
|
|
12028
|
+
const path24 = input.file_path ?? input.path;
|
|
12029
|
+
if (typeof path24 === "string") {
|
|
12030
|
+
return [{ label: "Path", value: path24 }];
|
|
11993
12031
|
}
|
|
11994
12032
|
}
|
|
11995
12033
|
return Object.entries(input).slice(0, 6).map(([key, value]) => ({
|
|
@@ -12891,7 +12929,7 @@ var init_registry6 = __esm({
|
|
|
12891
12929
|
init_registry5();
|
|
12892
12930
|
init_installer();
|
|
12893
12931
|
init_schedule_spec();
|
|
12894
|
-
SUPPORTED_API_VERSIONS = /* @__PURE__ */ new Set([CURRENT_PLUGIN_API_VERSION, "0.
|
|
12932
|
+
SUPPORTED_API_VERSIONS = /* @__PURE__ */ new Set([CURRENT_PLUGIN_API_VERSION, "0.21"]);
|
|
12895
12933
|
pluginCache = null;
|
|
12896
12934
|
lastLoadedPluginIds = /* @__PURE__ */ new Set();
|
|
12897
12935
|
PluginTableSchema = z16.object({
|
|
@@ -12934,8 +12972,8 @@ function pluginTools(_ctx) {
|
|
|
12934
12972
|
Promise.resolve().then(() => (init_ainative_paths(), ainative_paths_exports)),
|
|
12935
12973
|
Promise.resolve().then(() => (init_types(), types_exports))
|
|
12936
12974
|
]);
|
|
12937
|
-
const
|
|
12938
|
-
const
|
|
12975
|
+
const fs23 = await import("fs");
|
|
12976
|
+
const path24 = await import("path");
|
|
12939
12977
|
const yaml13 = await import("js-yaml");
|
|
12940
12978
|
const kind5 = listPlugins2();
|
|
12941
12979
|
const registrations = await listPluginMcpRegistrations2();
|
|
@@ -12952,13 +12990,13 @@ function pluginTools(_ctx) {
|
|
|
12952
12990
|
let manifestHash;
|
|
12953
12991
|
let capabilityAcceptStatus = "pending";
|
|
12954
12992
|
try {
|
|
12955
|
-
const pluginYamlPath =
|
|
12993
|
+
const pluginYamlPath = path24.join(
|
|
12956
12994
|
pluginsDir,
|
|
12957
12995
|
pluginId,
|
|
12958
12996
|
"plugin.yaml"
|
|
12959
12997
|
);
|
|
12960
|
-
if (
|
|
12961
|
-
const content =
|
|
12998
|
+
if (fs23.existsSync(pluginYamlPath)) {
|
|
12999
|
+
const content = fs23.readFileSync(pluginYamlPath, "utf-8");
|
|
12962
13000
|
const rawManifest = yaml13.load(content);
|
|
12963
13001
|
if (rawManifest !== null && typeof rawManifest === "object" && !Array.isArray(rawManifest)) {
|
|
12964
13002
|
const record = rawManifest;
|
|
@@ -12971,7 +13009,7 @@ function pluginTools(_ctx) {
|
|
|
12971
13009
|
try {
|
|
12972
13010
|
manifestHash = deriveManifestHash2(content);
|
|
12973
13011
|
const parsed = PluginManifestSchema2.safeParse(rawManifest);
|
|
12974
|
-
const pluginRootDir =
|
|
13012
|
+
const pluginRootDir = path24.join(pluginsDir, pluginId);
|
|
12975
13013
|
const check = isCapabilityAccepted2(
|
|
12976
13014
|
pluginId,
|
|
12977
13015
|
manifestHash,
|
|
@@ -18067,13 +18105,17 @@ ${learnedCtx}
|
|
|
18067
18105
|
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.` : "";
|
|
18068
18106
|
const systemInstructions = [worktreeNote, profileInstructions, learnedCtxBlock, docContext, tableContext, outputInstructions].filter(Boolean).join("\n\n");
|
|
18069
18107
|
const maxTurns = profile?.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
18108
|
+
const { modelId } = await resolvePreferredModel("claude-code", {
|
|
18109
|
+
pinnedModelId: profile?.capabilityOverrides?.["claude-code"]?.modelId
|
|
18110
|
+
});
|
|
18070
18111
|
return {
|
|
18071
18112
|
userPrompt: basePrompt,
|
|
18072
18113
|
systemInstructions,
|
|
18073
18114
|
cwd,
|
|
18074
18115
|
payload,
|
|
18075
18116
|
maxTurns,
|
|
18076
|
-
canUseToolPolicy: payload?.canUseToolPolicy
|
|
18117
|
+
canUseToolPolicy: payload?.canUseToolPolicy,
|
|
18118
|
+
modelId
|
|
18077
18119
|
};
|
|
18078
18120
|
}
|
|
18079
18121
|
async function executeClaudeTask(taskId) {
|
|
@@ -18119,6 +18161,10 @@ async function executeClaudeTask(taskId) {
|
|
|
18119
18161
|
prompt: ctx.userPrompt,
|
|
18120
18162
|
options: {
|
|
18121
18163
|
abortController,
|
|
18164
|
+
// Explicit model: profile pin > onboarding preference > quality
|
|
18165
|
+
// default. Omitting this let the SDK pick ITS default (Opus) and
|
|
18166
|
+
// silently bill the wrong tier.
|
|
18167
|
+
model: ctx.modelId,
|
|
18122
18168
|
includePartialMessages: true,
|
|
18123
18169
|
cwd: ctx.cwd,
|
|
18124
18170
|
env: buildClaudeSdkEnv(authEnv),
|
|
@@ -18241,6 +18287,9 @@ async function resumeClaudeTask(taskId) {
|
|
|
18241
18287
|
options: {
|
|
18242
18288
|
resume: task.sessionId,
|
|
18243
18289
|
abortController,
|
|
18290
|
+
// Same model resolution as the original run — a resume must not
|
|
18291
|
+
// silently hop tiers (profile pin > preference > quality default).
|
|
18292
|
+
model: ctx.modelId,
|
|
18244
18293
|
includePartialMessages: true,
|
|
18245
18294
|
cwd: ctx.cwd,
|
|
18246
18295
|
env: buildClaudeSdkEnv(authEnv),
|
|
@@ -18359,6 +18408,7 @@ var init_claude_agent = __esm({
|
|
|
18359
18408
|
init_context_builder2();
|
|
18360
18409
|
init_output_scanner();
|
|
18361
18410
|
init_registry2();
|
|
18411
|
+
init_model_preference();
|
|
18362
18412
|
init_compatibility();
|
|
18363
18413
|
init_claude_sdk();
|
|
18364
18414
|
init_types2();
|
|
@@ -18377,9 +18427,8 @@ var init_claude_agent = __esm({
|
|
|
18377
18427
|
// src/lib/agents/runtime/claude.ts
|
|
18378
18428
|
import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
|
|
18379
18429
|
import { eq as eq34 } from "drizzle-orm";
|
|
18380
|
-
function claudeCodeModelAlias() {
|
|
18381
|
-
|
|
18382
|
-
return models.tiers?.quality ?? models.default;
|
|
18430
|
+
async function claudeCodeModelAlias() {
|
|
18431
|
+
return (await resolvePreferredModel("claude-code")).modelId;
|
|
18383
18432
|
}
|
|
18384
18433
|
function buildTaskAssistSystemPrompt(profileIds) {
|
|
18385
18434
|
const profileList = profileIds.length > 0 ? `Available agent profiles: ${profileIds.join(", ")}
|
|
@@ -18473,7 +18522,7 @@ Provide a brief analysis (2-3 paragraphs max). Include specific terminology rele
|
|
|
18473
18522
|
prompt,
|
|
18474
18523
|
options: {
|
|
18475
18524
|
abortController,
|
|
18476
|
-
model: claudeCodeModelAlias(),
|
|
18525
|
+
model: await claudeCodeModelAlias(),
|
|
18477
18526
|
includePartialMessages: true,
|
|
18478
18527
|
env: buildClaudeSdkEnv(authEnv),
|
|
18479
18528
|
allowedTools: []
|
|
@@ -18614,7 +18663,7 @@ async function runMetaCompletion(input) {
|
|
|
18614
18663
|
prompt: input.prompt,
|
|
18615
18664
|
options: {
|
|
18616
18665
|
abortController,
|
|
18617
|
-
model: claudeCodeModelAlias(),
|
|
18666
|
+
model: await claudeCodeModelAlias(),
|
|
18618
18667
|
includePartialMessages: true,
|
|
18619
18668
|
cwd: getLaunchCwd(),
|
|
18620
18669
|
env: buildClaudeSdkEnv(authEnv),
|
|
@@ -18794,7 +18843,7 @@ ${userMessage}`;
|
|
|
18794
18843
|
prompt,
|
|
18795
18844
|
options: {
|
|
18796
18845
|
abortController,
|
|
18797
|
-
model: claudeCodeModelAlias(),
|
|
18846
|
+
model: await claudeCodeModelAlias(),
|
|
18798
18847
|
includePartialMessages: true,
|
|
18799
18848
|
cwd: getLaunchCwd(),
|
|
18800
18849
|
env: buildClaudeSdkEnv(authEnv),
|
|
@@ -18869,7 +18918,7 @@ ${userMessage}`;
|
|
|
18869
18918
|
prompt,
|
|
18870
18919
|
options: {
|
|
18871
18920
|
abortController,
|
|
18872
|
-
model: claudeCodeModelAlias(),
|
|
18921
|
+
model: await claudeCodeModelAlias(),
|
|
18873
18922
|
includePartialMessages: true,
|
|
18874
18923
|
cwd: getLaunchCwd(),
|
|
18875
18924
|
env: buildClaudeSdkEnv(authEnv),
|
|
@@ -18934,7 +18983,7 @@ async function testClaudeConnection() {
|
|
|
18934
18983
|
prompt: "Reply with exactly: OK",
|
|
18935
18984
|
options: {
|
|
18936
18985
|
abortController,
|
|
18937
|
-
model: claudeCodeModelAlias(),
|
|
18986
|
+
model: await claudeCodeModelAlias(),
|
|
18938
18987
|
maxTurns: 1,
|
|
18939
18988
|
includePartialMessages: false,
|
|
18940
18989
|
cwd: getLaunchCwd(),
|
|
@@ -18981,6 +19030,7 @@ var init_claude = __esm({
|
|
|
18981
19030
|
init_compatibility();
|
|
18982
19031
|
init_claude_agent();
|
|
18983
19032
|
init_catalog2();
|
|
19033
|
+
init_model_preference();
|
|
18984
19034
|
init_claude_sdk();
|
|
18985
19035
|
init_workspace_context();
|
|
18986
19036
|
init_helpers();
|
|
@@ -19387,13 +19437,13 @@ var init_codex_app_server_client = __esm({
|
|
|
19387
19437
|
await syncPluginMcpToCodex2();
|
|
19388
19438
|
} catch (err2) {
|
|
19389
19439
|
try {
|
|
19390
|
-
const
|
|
19391
|
-
const
|
|
19440
|
+
const fs23 = await import("fs");
|
|
19441
|
+
const path24 = await import("path");
|
|
19392
19442
|
const { getAinativeLogsDir: getAinativeLogsDir2 } = await Promise.resolve().then(() => (init_ainative_paths(), ainative_paths_exports));
|
|
19393
19443
|
const dir = getAinativeLogsDir2();
|
|
19394
|
-
|
|
19395
|
-
|
|
19396
|
-
|
|
19444
|
+
fs23.mkdirSync(dir, { recursive: true });
|
|
19445
|
+
fs23.appendFileSync(
|
|
19446
|
+
path24.join(dir, "plugins.log"),
|
|
19397
19447
|
`${(/* @__PURE__ */ new Date()).toISOString()} codex-sync-failed: ${err2 instanceof Error ? err2.message : String(err2)}
|
|
19398
19448
|
`
|
|
19399
19449
|
);
|
|
@@ -20875,7 +20925,8 @@ ${outputInstructions}`;
|
|
|
20875
20925
|
initialMessages = [{ role: "user", content: ctx.userPrompt }];
|
|
20876
20926
|
}
|
|
20877
20927
|
const { getSetting: getSetting2 } = await Promise.resolve().then(() => (init_helpers(), helpers_exports));
|
|
20878
|
-
const
|
|
20928
|
+
const { resolvePreferredModel: resolvePreferredModel2 } = await Promise.resolve().then(() => (init_model_preference(), model_preference_exports));
|
|
20929
|
+
const modelId = await getSetting2("anthropic_direct_model") ?? (await resolvePreferredModel2("anthropic-direct")).modelId;
|
|
20879
20930
|
const maxTurns = ctx.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
20880
20931
|
await db.insert(agentLogs).values({
|
|
20881
20932
|
id: crypto.randomUUID(),
|
|
@@ -21346,7 +21397,8 @@ ${outputInstructions}`;
|
|
|
21346
21397
|
);
|
|
21347
21398
|
const pluginMcpTools = mcpServersToOpenAiTools(mergedMcpServers);
|
|
21348
21399
|
const { getSetting: getSetting2 } = await Promise.resolve().then(() => (init_helpers(), helpers_exports));
|
|
21349
|
-
const
|
|
21400
|
+
const { resolvePreferredModel: resolvePreferredModel2 } = await Promise.resolve().then(() => (init_model_preference(), model_preference_exports));
|
|
21401
|
+
const modelId = await getSetting2("openai_direct_model") ?? (await resolvePreferredModel2("openai-direct")).modelId;
|
|
21350
21402
|
const maxTurns = ctx.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
21351
21403
|
let previousResponseId = null;
|
|
21352
21404
|
if (isResume) {
|
|
@@ -22186,6 +22238,17 @@ async function getUsageAggregates(policy, runtimeStates, now = /* @__PURE__ */ n
|
|
|
22186
22238
|
runtimes[runtimeId].daily.totalTokens += row.totalTokens ?? 0;
|
|
22187
22239
|
}
|
|
22188
22240
|
});
|
|
22241
|
+
const metered = {
|
|
22242
|
+
daily: { costMicros: 0, totalTokens: 0 },
|
|
22243
|
+
monthly: { costMicros: 0, totalTokens: 0 }
|
|
22244
|
+
};
|
|
22245
|
+
for (const runtimeId of SUPPORTED_AGENT_RUNTIMES) {
|
|
22246
|
+
metered.daily.costMicros += runtimes[runtimeId].daily.costMicros;
|
|
22247
|
+
metered.daily.totalTokens += runtimes[runtimeId].daily.totalTokens;
|
|
22248
|
+
metered.monthly.costMicros += runtimes[runtimeId].monthly.costMicros;
|
|
22249
|
+
metered.monthly.totalTokens += runtimes[runtimeId].monthly.totalTokens;
|
|
22250
|
+
}
|
|
22251
|
+
let planPricedMonthlyMicros = null;
|
|
22189
22252
|
if (runtimeStates["claude-code"].billingMode === "subscription") {
|
|
22190
22253
|
const planPriceUsd = await getClaudeOAuthPlanPrice(
|
|
22191
22254
|
policy.runtimes["claude-code"].claudeOAuthPlan
|
|
@@ -22194,6 +22257,7 @@ async function getUsageAggregates(policy, runtimeStates, now = /* @__PURE__ */ n
|
|
|
22194
22257
|
const dailyMicros = Math.round(monthlyMicros / daysInMonth(now));
|
|
22195
22258
|
runtimes["claude-code"].monthly.costMicros = monthlyMicros;
|
|
22196
22259
|
runtimes["claude-code"].daily.costMicros = dailyMicros;
|
|
22260
|
+
planPricedMonthlyMicros = monthlyMicros;
|
|
22197
22261
|
}
|
|
22198
22262
|
const overall = {
|
|
22199
22263
|
daily: { costMicros: 0, totalTokens: 0 },
|
|
@@ -22211,6 +22275,8 @@ async function getUsageAggregates(policy, runtimeStates, now = /* @__PURE__ */ n
|
|
|
22211
22275
|
return {
|
|
22212
22276
|
overall,
|
|
22213
22277
|
runtimes,
|
|
22278
|
+
metered,
|
|
22279
|
+
planPricedMonthlyMicros,
|
|
22214
22280
|
...getBudgetWindowBounds(now)
|
|
22215
22281
|
};
|
|
22216
22282
|
}
|
|
@@ -22985,14 +23051,14 @@ function resolvePostAction(action, row, itemVariable) {
|
|
|
22985
23051
|
function substituteRowPath(template, row, itemVariable) {
|
|
22986
23052
|
const escaped = itemVariable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
22987
23053
|
const pattern = new RegExp(`\\{\\{\\s*${escaped}\\.([\\w.]+)\\s*\\}\\}`, "g");
|
|
22988
|
-
return template.replace(pattern, (_match,
|
|
22989
|
-
const value = readPath(row,
|
|
23054
|
+
return template.replace(pattern, (_match, path24) => {
|
|
23055
|
+
const value = readPath(row, path24);
|
|
22990
23056
|
if (value === void 0 || value === null) return "";
|
|
22991
23057
|
return String(value);
|
|
22992
23058
|
});
|
|
22993
23059
|
}
|
|
22994
|
-
function readPath(obj,
|
|
22995
|
-
const parts =
|
|
23060
|
+
function readPath(obj, path24) {
|
|
23061
|
+
const parts = path24.split(".");
|
|
22996
23062
|
let current = obj;
|
|
22997
23063
|
for (const part of parts) {
|
|
22998
23064
|
if (current === null || current === void 0) return void 0;
|
|
@@ -23179,14 +23245,14 @@ ${resolvedTemplate}`);
|
|
|
23179
23245
|
return parts.join("");
|
|
23180
23246
|
}
|
|
23181
23247
|
function resolveRowTemplate(template, context) {
|
|
23182
|
-
return template.replace(/\{\{\s*([^}]+)\s*\}\}/g, (_match,
|
|
23183
|
-
const value = readContextPath(context,
|
|
23248
|
+
return template.replace(/\{\{\s*([^}]+)\s*\}\}/g, (_match, path24) => {
|
|
23249
|
+
const value = readContextPath(context, path24.trim());
|
|
23184
23250
|
if (value === void 0 || value === null) return "";
|
|
23185
23251
|
return typeof value === "string" ? value : JSON.stringify(value);
|
|
23186
23252
|
});
|
|
23187
23253
|
}
|
|
23188
|
-
function readContextPath(value,
|
|
23189
|
-
const parts =
|
|
23254
|
+
function readContextPath(value, path24) {
|
|
23255
|
+
const parts = path24.split(".");
|
|
23190
23256
|
let current = value;
|
|
23191
23257
|
for (const part of parts) {
|
|
23192
23258
|
if (current === null || current === void 0) return void 0;
|
|
@@ -25674,8 +25740,8 @@ import { execFileSync as execFileSync3 } from "child_process";
|
|
|
25674
25740
|
import yaml12 from "js-yaml";
|
|
25675
25741
|
import semver from "semver";
|
|
25676
25742
|
function relayCoreVersion() {
|
|
25677
|
-
if (semver.valid("0.
|
|
25678
|
-
return "0.
|
|
25743
|
+
if (semver.valid("0.22.1")) {
|
|
25744
|
+
return "0.22.1";
|
|
25679
25745
|
}
|
|
25680
25746
|
try {
|
|
25681
25747
|
const root = getAppRoot(import.meta.dirname, 3);
|
|
@@ -26014,6 +26080,68 @@ var init_install = __esm({
|
|
|
26014
26080
|
}
|
|
26015
26081
|
});
|
|
26016
26082
|
|
|
26083
|
+
// src/lib/licensing/recap.ts
|
|
26084
|
+
var recap_exports = {};
|
|
26085
|
+
__export(recap_exports, {
|
|
26086
|
+
changelogWindow: () => changelogWindow,
|
|
26087
|
+
entitledPackRecaps: () => entitledPackRecaps
|
|
26088
|
+
});
|
|
26089
|
+
import fs21 from "fs";
|
|
26090
|
+
import path22 from "path";
|
|
26091
|
+
import semver2 from "semver";
|
|
26092
|
+
function changelogWindow(changelog, fromExclusive, toInclusive) {
|
|
26093
|
+
if (!changelog || !toInclusive || !semver2.valid(toInclusive)) return [];
|
|
26094
|
+
const from = fromExclusive && semver2.valid(fromExclusive) ? fromExclusive : null;
|
|
26095
|
+
return Object.entries(changelog).filter(([version]) => semver2.valid(version)).filter(
|
|
26096
|
+
([version]) => semver2.compare(version, toInclusive) <= 0 && (from === null || semver2.compare(version, from) > 0)
|
|
26097
|
+
).sort(([a], [b]) => semver2.compare(a, b)).map(([version, note]) => ({ version, note }));
|
|
26098
|
+
}
|
|
26099
|
+
function entitledPackRecaps(entitlements, opts2 = {}) {
|
|
26100
|
+
try {
|
|
26101
|
+
const appsDir = opts2.appsDir ?? getAinativeAppsDir();
|
|
26102
|
+
const covered = new Set(entitlements);
|
|
26103
|
+
const out = [];
|
|
26104
|
+
for (const tpl of listPackTemplates({ templatesDir: opts2.templatesDir })) {
|
|
26105
|
+
if (tpl.error || !tpl.meta?.entitlement) continue;
|
|
26106
|
+
if (!covered.has(tpl.meta.entitlement)) continue;
|
|
26107
|
+
if (!fs21.existsSync(path22.join(appsDir, tpl.id))) continue;
|
|
26108
|
+
const avail = packUpdateAvailability(tpl.id, {
|
|
26109
|
+
appsDir,
|
|
26110
|
+
templatesDir: opts2.templatesDir
|
|
26111
|
+
});
|
|
26112
|
+
const state = readInstallState(appsDir, tpl.id);
|
|
26113
|
+
const changelog = tpl.meta.changelog;
|
|
26114
|
+
out.push({
|
|
26115
|
+
packId: tpl.id,
|
|
26116
|
+
packName: tpl.meta.name,
|
|
26117
|
+
installedVersion: avail.installedVersion,
|
|
26118
|
+
...state?.installedAt ? { installedAt: state.installedAt } : {},
|
|
26119
|
+
availableVersion: avail.availableVersion,
|
|
26120
|
+
updateAvailable: avail.updateAvailable,
|
|
26121
|
+
...avail.installedVersion && changelog?.[avail.installedVersion] ? { received: changelog[avail.installedVersion] } : {},
|
|
26122
|
+
pending: avail.updateAvailable ? changelogWindow(
|
|
26123
|
+
changelog,
|
|
26124
|
+
avail.installedVersion,
|
|
26125
|
+
avail.availableVersion
|
|
26126
|
+
) : [],
|
|
26127
|
+
...tpl.meta.purchaseUrl ? { purchaseUrl: tpl.meta.purchaseUrl } : {}
|
|
26128
|
+
});
|
|
26129
|
+
}
|
|
26130
|
+
return out;
|
|
26131
|
+
} catch {
|
|
26132
|
+
return [];
|
|
26133
|
+
}
|
|
26134
|
+
}
|
|
26135
|
+
var init_recap = __esm({
|
|
26136
|
+
"src/lib/licensing/recap.ts"() {
|
|
26137
|
+
"use strict";
|
|
26138
|
+
init_ainative_paths();
|
|
26139
|
+
init_catalog();
|
|
26140
|
+
init_update();
|
|
26141
|
+
init_install_state();
|
|
26142
|
+
}
|
|
26143
|
+
});
|
|
26144
|
+
|
|
26017
26145
|
// src/lib/packs/update.ts
|
|
26018
26146
|
var update_exports = {};
|
|
26019
26147
|
__export(update_exports, {
|
|
@@ -26021,15 +26149,15 @@ __export(update_exports, {
|
|
|
26021
26149
|
packUpdateAvailability: () => packUpdateAvailability,
|
|
26022
26150
|
updatePack: () => updatePack
|
|
26023
26151
|
});
|
|
26024
|
-
import
|
|
26025
|
-
import
|
|
26026
|
-
import
|
|
26152
|
+
import fs22 from "fs";
|
|
26153
|
+
import path23 from "path";
|
|
26154
|
+
import semver3 from "semver";
|
|
26027
26155
|
function packUpdateAvailability(appId, opts2 = {}) {
|
|
26028
26156
|
const appsDir = opts2.appsDir ?? getAinativeAppsDir();
|
|
26029
26157
|
const installedVersion = readInstallState(appsDir, appId)?.packVersion ?? null;
|
|
26030
26158
|
const template = findPackTemplate(appId, { templatesDir: opts2.templatesDir });
|
|
26031
26159
|
const availableVersion = template?.meta?.version ?? null;
|
|
26032
|
-
const updateAvailable = availableVersion !== null &&
|
|
26160
|
+
const updateAvailable = availableVersion !== null && semver3.valid(availableVersion) !== null && (installedVersion === null || semver3.valid(installedVersion) === null || semver3.compare(availableVersion, installedVersion) > 0);
|
|
26033
26161
|
return { installedVersion, availableVersion, updateAvailable };
|
|
26034
26162
|
}
|
|
26035
26163
|
async function updatePack(id, options = {}) {
|
|
@@ -26057,13 +26185,13 @@ async function updatePack(id, options = {}) {
|
|
|
26057
26185
|
);
|
|
26058
26186
|
}
|
|
26059
26187
|
const coreVersion = options.coreVersion ?? relayCoreVersion();
|
|
26060
|
-
if (pack.meta.relayCore && !
|
|
26188
|
+
if (pack.meta.relayCore && !semver3.satisfies(coreVersion, pack.meta.relayCore)) {
|
|
26061
26189
|
throw new PackValidationError(
|
|
26062
26190
|
`Pack ${pack.meta.id}@${pack.meta.version} requires relay-core ${pack.meta.relayCore}, but this install is ${coreVersion}.`
|
|
26063
26191
|
);
|
|
26064
26192
|
}
|
|
26065
26193
|
const newVersion = pack.meta.version;
|
|
26066
|
-
if (previousVersion !== null &&
|
|
26194
|
+
if (previousVersion !== null && semver3.valid(previousVersion) && semver3.valid(newVersion) && semver3.compare(newVersion, previousVersion) <= 0) {
|
|
26067
26195
|
return {
|
|
26068
26196
|
packId: id,
|
|
26069
26197
|
previousVersion,
|
|
@@ -26088,8 +26216,21 @@ async function updatePack(id, options = {}) {
|
|
|
26088
26216
|
} catch (err2) {
|
|
26089
26217
|
if (err2 instanceof PackLicenseError2) {
|
|
26090
26218
|
const renew = pack.meta.purchaseUrl ? `renew at ${pack.meta.purchaseUrl}` : `redeem one with: relay license add <path-or-url to your .license.json>`;
|
|
26219
|
+
let withheld = "";
|
|
26220
|
+
try {
|
|
26221
|
+
const { changelogWindow: changelogWindow2 } = await Promise.resolve().then(() => (init_recap(), recap_exports));
|
|
26222
|
+
const pending = changelogWindow2(
|
|
26223
|
+
pack.meta.changelog,
|
|
26224
|
+
previousVersion,
|
|
26225
|
+
newVersion
|
|
26226
|
+
);
|
|
26227
|
+
if (pending.length > 0) {
|
|
26228
|
+
withheld = `This update includes: ` + pending.map((p) => `v${p.version} \u2014 ${p.note}`).join("; ") + ` `;
|
|
26229
|
+
}
|
|
26230
|
+
} catch {
|
|
26231
|
+
}
|
|
26091
26232
|
throw new PackLicenseError2(
|
|
26092
|
-
`Your installed ${id} keeps working \u2014 nothing is locked. Updating to v${newVersion} needs an active license: ${renew}. (${err2.message})`,
|
|
26233
|
+
`Your installed ${id} keeps working \u2014 nothing is locked. Updating to v${newVersion} needs an active license: ${renew}. ` + withheld + `(${err2.message})`,
|
|
26093
26234
|
err2.reason
|
|
26094
26235
|
);
|
|
26095
26236
|
}
|
|
@@ -26097,7 +26238,7 @@ async function updatePack(id, options = {}) {
|
|
|
26097
26238
|
}
|
|
26098
26239
|
}
|
|
26099
26240
|
const resolved = resolvePackLayer(pack);
|
|
26100
|
-
const backupRoot =
|
|
26241
|
+
const backupRoot = path23.join(
|
|
26101
26242
|
appsDir,
|
|
26102
26243
|
id,
|
|
26103
26244
|
"backup",
|
|
@@ -26106,12 +26247,12 @@ async function updatePack(id, options = {}) {
|
|
|
26106
26247
|
const backedUp = [];
|
|
26107
26248
|
for (const file of resolved.files) {
|
|
26108
26249
|
const dest = artifactDestPath(file.relPath, profilesDir, blueprintsDir);
|
|
26109
|
-
if (!dest || !
|
|
26250
|
+
if (!dest || !fs22.existsSync(dest)) continue;
|
|
26110
26251
|
const recorded = state?.files[file.relPath];
|
|
26111
26252
|
if (recorded !== void 0 && hashFileSha256(dest) === recorded) continue;
|
|
26112
|
-
const backupPath =
|
|
26113
|
-
|
|
26114
|
-
|
|
26253
|
+
const backupPath = path23.join(backupRoot, file.relPath);
|
|
26254
|
+
fs22.mkdirSync(path23.dirname(backupPath), { recursive: true });
|
|
26255
|
+
fs22.copyFileSync(dest, backupPath);
|
|
26115
26256
|
backedUp.push(file.relPath);
|
|
26116
26257
|
}
|
|
26117
26258
|
const install = await installPack(packDir, {
|
|
@@ -26395,6 +26536,17 @@ async function runAdd2(source, io) {
|
|
|
26395
26536
|
return 1;
|
|
26396
26537
|
}
|
|
26397
26538
|
}
|
|
26539
|
+
async function pendingRecaps(entitlements, io) {
|
|
26540
|
+
try {
|
|
26541
|
+
const { entitledPackRecaps: entitledPackRecaps2 } = await Promise.resolve().then(() => (init_recap(), recap_exports));
|
|
26542
|
+
return entitledPackRecaps2(entitlements, {
|
|
26543
|
+
appsDir: io.appsDir,
|
|
26544
|
+
templatesDir: io.templatesDir
|
|
26545
|
+
}).filter((r) => r.pending.length > 0);
|
|
26546
|
+
} catch {
|
|
26547
|
+
return [];
|
|
26548
|
+
}
|
|
26549
|
+
}
|
|
26398
26550
|
async function runStatus(io) {
|
|
26399
26551
|
try {
|
|
26400
26552
|
const { listLicenses: listLicenses2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
@@ -26418,6 +26570,16 @@ async function runStatus(io) {
|
|
|
26418
26570
|
io.log(
|
|
26419
26571
|
` Status: ${lic.valid ? "valid" : `invalid \u2014 ${lic.reason ?? "corrupt entry"}`}`
|
|
26420
26572
|
);
|
|
26573
|
+
const recaps = await pendingRecaps(lic.entitlements, io);
|
|
26574
|
+
if (lic.valid && recaps.length > 0) {
|
|
26575
|
+
io.log(` Included in your term, waiting to install:`);
|
|
26576
|
+
for (const r of recaps) {
|
|
26577
|
+
for (const p of r.pending) {
|
|
26578
|
+
io.log(` ${r.packName} v${p.version} \u2014 ${p.note}`);
|
|
26579
|
+
}
|
|
26580
|
+
io.log(` \u2192 relay pack update ${r.packId}`);
|
|
26581
|
+
}
|
|
26582
|
+
}
|
|
26421
26583
|
if (lic.valid && lic.expiresAt) {
|
|
26422
26584
|
const daysLeft = Math.floor(
|
|
26423
26585
|
(new Date(lic.expiresAt).getTime() - now.getTime()) / DAY_MS
|
|
@@ -26426,6 +26588,23 @@ async function runStatus(io) {
|
|
|
26426
26588
|
io.log(
|
|
26427
26589
|
` \u26A0 Renewal: expires in ${daysLeft} day${daysLeft === 1 ? "" : "s"}. Your installed packs are yours forever; renewing keeps new premium packs and updates flowing.`
|
|
26428
26590
|
);
|
|
26591
|
+
const latest = recaps.map((r) => ({ r, p: r.pending[r.pending.length - 1] })).filter((x) => x.p);
|
|
26592
|
+
for (const { r, p } of latest) {
|
|
26593
|
+
io.log(
|
|
26594
|
+
` This license year delivered ${r.packName} v${p.version} \u2014 ${p.note}`
|
|
26595
|
+
);
|
|
26596
|
+
}
|
|
26597
|
+
}
|
|
26598
|
+
}
|
|
26599
|
+
if (!lic.valid && /expired/i.test(lic.reason ?? "") && recaps.length > 0) {
|
|
26600
|
+
io.log(
|
|
26601
|
+
` Your installed packs keep working \u2014 nothing is locked. Renewing unlocks:`
|
|
26602
|
+
);
|
|
26603
|
+
for (const r of recaps) {
|
|
26604
|
+
for (const p of r.pending) {
|
|
26605
|
+
io.log(` ${r.packName} v${p.version} \u2014 ${p.note}`);
|
|
26606
|
+
}
|
|
26607
|
+
if (r.purchaseUrl) io.log(` \u2192 renew at ${r.purchaseUrl}`);
|
|
26429
26608
|
}
|
|
26430
26609
|
}
|
|
26431
26610
|
io.log("");
|
|
@@ -26796,10 +26975,10 @@ import { existsSync as existsSync4, renameSync, cpSync, rmSync as rmSync2, readF
|
|
|
26796
26975
|
import { join as join6 } from "path";
|
|
26797
26976
|
import { homedir as homedir2 } from "os";
|
|
26798
26977
|
import Database from "better-sqlite3";
|
|
26799
|
-
function hasSqliteHeader(
|
|
26978
|
+
function hasSqliteHeader(path24) {
|
|
26800
26979
|
const SQLITE_MAGIC = "SQLite format 3\0";
|
|
26801
26980
|
try {
|
|
26802
|
-
const header = readFileSync3(
|
|
26981
|
+
const header = readFileSync3(path24, { encoding: null });
|
|
26803
26982
|
return header.length >= 16 && header.subarray(0, 16).toString("binary") === SQLITE_MAGIC;
|
|
26804
26983
|
} catch {
|
|
26805
26984
|
return false;
|
package/package.json
CHANGED
|
@@ -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,
|