@gethelio/proxy 0.7.0 → 0.8.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.
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta name="referrer" content="no-referrer" />
|
|
7
7
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
8
8
|
<title>Helio Dashboard</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-0ylAcvX3.js"></script>
|
|
10
10
|
<link rel="stylesheet" crossorigin href="/assets/index-DZKoV0Vx.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
package/dist/index.d.ts
CHANGED
|
@@ -946,7 +946,7 @@ interface AuditTimeBucket {
|
|
|
946
946
|
interface AuditAggregateStats {
|
|
947
947
|
/** Total number of records in the time range. */
|
|
948
948
|
readonly total: number;
|
|
949
|
-
/** Total records that resolved without a block (`block_reason IS NULL`). */
|
|
949
|
+
/** Total records that resolved without a block (`block_reason IS NULL`), excluding drift events. */
|
|
950
950
|
readonly allowed_total: number;
|
|
951
951
|
/** Total records that resolved with a block (`block_reason IS NOT NULL`). */
|
|
952
952
|
readonly blocked_total: number;
|
package/dist/index.js
CHANGED
|
@@ -3141,6 +3141,7 @@ var GovernedForwarder = class {
|
|
|
3141
3141
|
let result;
|
|
3142
3142
|
let approvalOutcome;
|
|
3143
3143
|
let approvalWaitMs = 0;
|
|
3144
|
+
let approvalContext;
|
|
3144
3145
|
let rateLimitResult;
|
|
3145
3146
|
let spendLimitResult;
|
|
3146
3147
|
let forwardingError;
|
|
@@ -3170,6 +3171,7 @@ var GovernedForwarder = class {
|
|
|
3170
3171
|
result = approvalResult.result;
|
|
3171
3172
|
approvalOutcome = approvalResult.outcome;
|
|
3172
3173
|
approvalWaitMs = approvalResult.approvalWaitMs;
|
|
3174
|
+
approvalContext = approvalResult.approvalContext;
|
|
3173
3175
|
}
|
|
3174
3176
|
} else if (decision.action === "rate_limit") {
|
|
3175
3177
|
if (!this.rateLimiter) {
|
|
@@ -3222,6 +3224,7 @@ var GovernedForwarder = class {
|
|
|
3222
3224
|
dependencyResult,
|
|
3223
3225
|
evidenceBlocked,
|
|
3224
3226
|
approvalOutcome,
|
|
3227
|
+
approvalContext,
|
|
3225
3228
|
rateLimitResult,
|
|
3226
3229
|
spendLimitResult,
|
|
3227
3230
|
isDryRun,
|
|
@@ -3243,6 +3246,16 @@ var GovernedForwarder = class {
|
|
|
3243
3246
|
request.signal
|
|
3244
3247
|
);
|
|
3245
3248
|
const approvalWaitMs = performance.now() - approvalStart;
|
|
3249
|
+
const ticket = outcome.ticketId ? router.getTicket(outcome.ticketId) : void 0;
|
|
3250
|
+
const denialReason = outcome.status === "denied" && outcome.reason ? outcome.reason : void 0;
|
|
3251
|
+
const approvalContext = outcome.ticketId && (denialReason || ticket?.escalated_at) ? {
|
|
3252
|
+
ticket_id: outcome.ticketId,
|
|
3253
|
+
...denialReason ? { denial_reason: denialReason } : {},
|
|
3254
|
+
...ticket?.escalated_at ? {
|
|
3255
|
+
escalated_at: ticket.escalated_at,
|
|
3256
|
+
escalated_to: [...ticket.escalated_to ?? []]
|
|
3257
|
+
} : {}
|
|
3258
|
+
} : void 0;
|
|
3246
3259
|
let result;
|
|
3247
3260
|
if (outcome.status === "approved" || outcome.status === "break_glass") {
|
|
3248
3261
|
if (request.signal?.aborted) {
|
|
@@ -3271,7 +3284,7 @@ var GovernedForwarder = class {
|
|
|
3271
3284
|
result = makeErrorResult(request, POLICY_DENIED, message, { ...feedback });
|
|
3272
3285
|
}
|
|
3273
3286
|
}
|
|
3274
|
-
return { result, outcome, approvalWaitMs };
|
|
3287
|
+
return { result, outcome, approvalWaitMs, approvalContext };
|
|
3275
3288
|
}
|
|
3276
3289
|
async handleRateLimit(request, decision, toolName) {
|
|
3277
3290
|
const limiter = this.rateLimiter;
|
|
@@ -3451,7 +3464,7 @@ var GovernedForwarder = class {
|
|
|
3451
3464
|
wasForwardedUpstream(decision, approvalOutcome, rateLimitResult, spendLimitResult) {
|
|
3452
3465
|
return decision.action === "allow" || approvalOutcome?.status === "approved" || approvalOutcome?.status === "break_glass" || approvalOutcome?.status === "timeout" && this.approvalRouter?.defaultOnTimeout === "allow" || rateLimitResult?.allowed === true || spendLimitResult?.allowed === true;
|
|
3453
3466
|
}
|
|
3454
|
-
writeAuditRecord(request, timestamp, toolName, toolArguments, decision, result, totalDurationMs, approvalWaitMs, flaggedDestructive, evidenceResult, dependencyResult, evidenceBlocked, approvalOutcome, rateLimitResult, spendLimitResult, isDryRun, forwardingError, drift) {
|
|
3467
|
+
writeAuditRecord(request, timestamp, toolName, toolArguments, decision, result, totalDurationMs, approvalWaitMs, flaggedDestructive, evidenceResult, dependencyResult, evidenceBlocked, approvalOutcome, approvalContext, rateLimitResult, spendLimitResult, isDryRun, forwardingError, drift) {
|
|
3455
3468
|
if (!this.auditWriter) return;
|
|
3456
3469
|
const wasForwarded = this.wasForwardedUpstream(
|
|
3457
3470
|
decision,
|
|
@@ -3486,6 +3499,12 @@ var GovernedForwarder = class {
|
|
|
3486
3499
|
}
|
|
3487
3500
|
};
|
|
3488
3501
|
}
|
|
3502
|
+
if (approvalContext) {
|
|
3503
|
+
evidenceChain = {
|
|
3504
|
+
...evidenceChain ?? {},
|
|
3505
|
+
approval: { ...approvalContext }
|
|
3506
|
+
};
|
|
3507
|
+
}
|
|
3489
3508
|
if (rateLimitResult) {
|
|
3490
3509
|
evidenceChain = {
|
|
3491
3510
|
...evidenceChain ?? {},
|
|
@@ -4536,6 +4555,7 @@ var EvidenceStore = class _EvidenceStore {
|
|
|
4536
4555
|
// src/evidence/api.ts
|
|
4537
4556
|
import { Hono as Hono5 } from "hono";
|
|
4538
4557
|
import { bodyLimit } from "hono/body-limit";
|
|
4558
|
+
import { HTTPException } from "hono/http-exception";
|
|
4539
4559
|
import { z as z5 } from "zod";
|
|
4540
4560
|
|
|
4541
4561
|
// src/auth/bearer.ts
|
|
@@ -4736,6 +4756,11 @@ function createSidebandApp(store, options = {}) {
|
|
|
4736
4756
|
const app = new Hono5();
|
|
4737
4757
|
const sdkToken = options.token && options.token.length > 0 ? options.token : void 0;
|
|
4738
4758
|
const adapterToken = options.adapterToken && options.adapterToken.length > 0 ? options.adapterToken : void 0;
|
|
4759
|
+
app.onError((err, c) => {
|
|
4760
|
+
if (err instanceof HTTPException) return err.getResponse();
|
|
4761
|
+
console.error("[helio] Unhandled sideband API error:", err);
|
|
4762
|
+
return c.json({ error: "Internal server error" }, 500);
|
|
4763
|
+
});
|
|
4739
4764
|
app.use("*", async (c, next) => {
|
|
4740
4765
|
const origin = c.req.header("origin");
|
|
4741
4766
|
if (origin) {
|
|
@@ -5113,6 +5138,7 @@ var GovernanceService = class {
|
|
|
5113
5138
|
}
|
|
5114
5139
|
let approvalStatus = null;
|
|
5115
5140
|
let approvedBy = null;
|
|
5141
|
+
let approvalContext;
|
|
5116
5142
|
if (entry.approvalTicketId) {
|
|
5117
5143
|
const ticket = this.getTicketStatus(entry.approvalTicketId);
|
|
5118
5144
|
const status = ticket?.status;
|
|
@@ -5121,6 +5147,16 @@ var GovernanceService = class {
|
|
|
5121
5147
|
}
|
|
5122
5148
|
approvalStatus = status;
|
|
5123
5149
|
approvedBy = ticket.resolved_by ?? null;
|
|
5150
|
+
if (ticket.denial_reason || ticket.escalated_at) {
|
|
5151
|
+
approvalContext = {
|
|
5152
|
+
ticket_id: entry.approvalTicketId,
|
|
5153
|
+
...ticket.denial_reason ? { denial_reason: ticket.denial_reason } : {},
|
|
5154
|
+
...ticket.escalated_at ? {
|
|
5155
|
+
escalated_at: ticket.escalated_at,
|
|
5156
|
+
escalated_to: [...ticket.escalated_to ?? []]
|
|
5157
|
+
} : {}
|
|
5158
|
+
};
|
|
5159
|
+
}
|
|
5124
5160
|
}
|
|
5125
5161
|
if (req.actual_amount !== void 0) {
|
|
5126
5162
|
if (!Number.isFinite(req.actual_amount) || req.actual_amount < 0) {
|
|
@@ -5157,6 +5193,7 @@ var GovernanceService = class {
|
|
|
5157
5193
|
limitsChain,
|
|
5158
5194
|
approvalStatus,
|
|
5159
5195
|
approvedBy,
|
|
5196
|
+
approvalContext,
|
|
5160
5197
|
upstreamError: req.status === "error" ? req.error ?? "tool call failed" : null,
|
|
5161
5198
|
upstreamResponse: req.result ?? null,
|
|
5162
5199
|
upstreamLatencyMs: req.duration_ms ?? null
|
|
@@ -5562,6 +5599,9 @@ var GovernanceService = class {
|
|
|
5562
5599
|
if (args.sidebandUnreported) {
|
|
5563
5600
|
evidenceChain = { ...evidenceChain ?? {}, sideband: { unreported: true } };
|
|
5564
5601
|
}
|
|
5602
|
+
if (args.approvalContext) {
|
|
5603
|
+
evidenceChain = { ...evidenceChain ?? {}, approval: { ...args.approvalContext } };
|
|
5604
|
+
}
|
|
5565
5605
|
const record = {
|
|
5566
5606
|
timestamp: args.timestampIso,
|
|
5567
5607
|
session_id: args.sessionId,
|
|
@@ -7231,6 +7271,7 @@ import { readFileSync } from "fs";
|
|
|
7231
7271
|
import { join } from "path";
|
|
7232
7272
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
7233
7273
|
import { Hono as Hono8 } from "hono";
|
|
7274
|
+
import { HTTPException as HTTPException2 } from "hono/http-exception";
|
|
7234
7275
|
import { z as z8 } from "zod";
|
|
7235
7276
|
import { cors } from "hono/cors";
|
|
7236
7277
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
@@ -7497,6 +7538,16 @@ function shouldSetSecureCookie(url, xForwardedProto) {
|
|
|
7497
7538
|
if (xForwardedProto?.toLowerCase() === "https") return true;
|
|
7498
7539
|
return new URL(url).protocol === "https:";
|
|
7499
7540
|
}
|
|
7541
|
+
function isPrivateIpv4(host) {
|
|
7542
|
+
const match = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
|
|
7543
|
+
if (!match) return false;
|
|
7544
|
+
const a = Number(match[1]);
|
|
7545
|
+
const b = Number(match[2]);
|
|
7546
|
+
const c = Number(match[3]);
|
|
7547
|
+
const d = Number(match[4]);
|
|
7548
|
+
if (a > 255 || b > 255 || c > 255 || d > 255) return false;
|
|
7549
|
+
return a === 10 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168;
|
|
7550
|
+
}
|
|
7500
7551
|
function createDashboardAppWithLifecycle(deps, options) {
|
|
7501
7552
|
const {
|
|
7502
7553
|
auditStore,
|
|
@@ -7510,6 +7561,11 @@ function createDashboardAppWithLifecycle(deps, options) {
|
|
|
7510
7561
|
const apiSecret = options?.apiSecret;
|
|
7511
7562
|
const sessionStore = apiSecret ? new DashboardSessionStore({ secret: apiSecret, ttlMs: SESSION_TTL_MS }) : void 0;
|
|
7512
7563
|
const app = new Hono8();
|
|
7564
|
+
app.onError((err, c) => {
|
|
7565
|
+
if (err instanceof HTTPException2) return err.getResponse();
|
|
7566
|
+
console.error("[helio] Unhandled dashboard API error:", err);
|
|
7567
|
+
return c.json({ error: "Internal server error" }, 500);
|
|
7568
|
+
});
|
|
7513
7569
|
app.use(
|
|
7514
7570
|
"*",
|
|
7515
7571
|
cors({
|
|
@@ -7519,7 +7575,7 @@ function createDashboardAppWithLifecycle(deps, options) {
|
|
|
7519
7575
|
const url = new URL(origin);
|
|
7520
7576
|
const h = url.hostname;
|
|
7521
7577
|
if (h === "localhost" || h === "127.0.0.1" || h === "0.0.0.0") return origin;
|
|
7522
|
-
if (
|
|
7578
|
+
if (isPrivateIpv4(h)) return origin;
|
|
7523
7579
|
} catch {
|
|
7524
7580
|
}
|
|
7525
7581
|
return null;
|