kifaru 1.0.177 → 1.0.178
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/dashboard/assurance.html +26 -8
- package/kifaru-gateway.js +151 -37
- package/kifaru-pi.js +2 -2
- package/package.json +2 -2
package/dashboard/assurance.html
CHANGED
|
@@ -2489,7 +2489,7 @@ ${f.dpa.map((d) => " - " + d).join("\n")}`
|
|
|
2489
2489
|
)
|
|
2490
2490
|
}
|
|
2491
2491
|
function startRun() {
|
|
2492
|
-
|
|
2492
|
+
signedIn() ? (window.location.href = "/assessments") : openLogin()
|
|
2493
2493
|
}
|
|
2494
2494
|
|
|
2495
2495
|
function switchTab(name) {
|
|
@@ -2543,17 +2543,33 @@ ${f.dpa.map((d) => " - " + d).join("\n")}`
|
|
|
2543
2543
|
return ""
|
|
2544
2544
|
}
|
|
2545
2545
|
}
|
|
2546
|
+
// The loopback dev sign-in establishes an httpOnly cookie session; no
|
|
2547
|
+
// bearer token is returned, so this flag tracks that it succeeded.
|
|
2548
|
+
function devSessionActive() {
|
|
2549
|
+
try {
|
|
2550
|
+
return localStorage.getItem("kifaru_dev_session") === "1"
|
|
2551
|
+
} catch (_) {
|
|
2552
|
+
return false
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
function setDevSession(on) {
|
|
2556
|
+
try {
|
|
2557
|
+
on ? localStorage.setItem("kifaru_dev_session", "1") : localStorage.removeItem("kifaru_dev_session")
|
|
2558
|
+
} catch (_) {}
|
|
2559
|
+
}
|
|
2560
|
+
function signedIn() {
|
|
2561
|
+
return Boolean(getToken()) || devSessionActive()
|
|
2562
|
+
}
|
|
2546
2563
|
async function autoDiscoverToken() {
|
|
2547
2564
|
let t = getToken()
|
|
2548
|
-
if (t) return t
|
|
2565
|
+
if (t || devSessionActive()) return t
|
|
2549
2566
|
try {
|
|
2550
|
-
|
|
2567
|
+
// Session-scoped dev sign-in: the gateway sets an httpOnly session
|
|
2568
|
+
// cookie for loopback browsers and returns no token.
|
|
2569
|
+
const r = await fetch("/api/v1/auth/dev-token", { credentials: "include" })
|
|
2551
2570
|
if (r.ok) {
|
|
2552
2571
|
const d = await r.json()
|
|
2553
|
-
if (d?.
|
|
2554
|
-
setToken(d.token)
|
|
2555
|
-
return d.token
|
|
2556
|
-
}
|
|
2572
|
+
if (d?.ok) setDevSession(true)
|
|
2557
2573
|
}
|
|
2558
2574
|
} catch (_) {}
|
|
2559
2575
|
return ""
|
|
@@ -2565,7 +2581,7 @@ ${f.dpa.map((d) => " - " + d).join("\n")}`
|
|
|
2565
2581
|
}
|
|
2566
2582
|
function openLogin() {
|
|
2567
2583
|
$("#loginErr").textContent = ""
|
|
2568
|
-
$("#logoutBtn").hidden = !
|
|
2584
|
+
$("#logoutBtn").hidden = !signedIn()
|
|
2569
2585
|
$("#loginModal").hidden = false
|
|
2570
2586
|
setTimeout(() => $("#loginEmail").focus(), 30)
|
|
2571
2587
|
}
|
|
@@ -2658,6 +2674,8 @@ ${f.dpa.map((d) => " - " + d).join("\n")}`
|
|
|
2658
2674
|
}
|
|
2659
2675
|
function signOut() {
|
|
2660
2676
|
setToken("")
|
|
2677
|
+
setDevSession(false)
|
|
2678
|
+
fetch("/api/v1/auth/logout", { method: "POST", credentials: "include" }).catch(() => {})
|
|
2661
2679
|
LIVE = null
|
|
2662
2680
|
ACTIVE_RUN = null
|
|
2663
2681
|
findingDetailCache.clear()
|
package/kifaru-gateway.js
CHANGED
|
@@ -27012,6 +27012,7 @@ function createAuthApp(input2) {
|
|
|
27012
27012
|
providers.github = createGitHubLoginProvider(input2.config.githubOAuth);
|
|
27013
27013
|
}
|
|
27014
27014
|
providers.local = createLocalProvider(input2.storage);
|
|
27015
|
+
providers.devlocal = createDevLocalProvider(input2.storage);
|
|
27015
27016
|
return issuer({
|
|
27016
27017
|
subjects,
|
|
27017
27018
|
providers,
|
|
@@ -27022,7 +27023,7 @@ function createAuthApp(input2) {
|
|
|
27022
27023
|
},
|
|
27023
27024
|
async success(ctx, response) {
|
|
27024
27025
|
const identity = await resolveIdentity(response);
|
|
27025
|
-
if (!isAllowedEmail(identity.email, input2.config)) {
|
|
27026
|
+
if (response.provider !== "devlocal" && !isAllowedEmail(identity.email, input2.config)) {
|
|
27026
27027
|
throw new Error(`Email ${identity.email} is not allowed for this alpha.`);
|
|
27027
27028
|
}
|
|
27028
27029
|
const role = identity.role ?? (response.provider === "bootstrap" && input2.config.bootstrapRole ? input2.config.bootstrapRole : input2.config.adminEmails.has(identity.email) ? "admin" : input2.config.defaultRole);
|
|
@@ -27145,7 +27146,7 @@ async function resolveIdentity(response) {
|
|
|
27145
27146
|
name: readString(response.name)
|
|
27146
27147
|
};
|
|
27147
27148
|
}
|
|
27148
|
-
if (response.provider === "local") {
|
|
27149
|
+
if (response.provider === "local" || response.provider === "devlocal") {
|
|
27149
27150
|
return {
|
|
27150
27151
|
subject: readString(response.subject) ?? response.email,
|
|
27151
27152
|
email: String(response.email).toLowerCase(),
|
|
@@ -27465,6 +27466,30 @@ function createLocalProvider(storage) {
|
|
|
27465
27466
|
}
|
|
27466
27467
|
};
|
|
27467
27468
|
}
|
|
27469
|
+
function createDevLocalProvider(storage) {
|
|
27470
|
+
return {
|
|
27471
|
+
type: "local",
|
|
27472
|
+
init() {},
|
|
27473
|
+
async client(input2) {
|
|
27474
|
+
const ticket = readString(input2.clientSecret);
|
|
27475
|
+
if (!ticket)
|
|
27476
|
+
throw new Error("Local dev ticket is required.");
|
|
27477
|
+
const stored = await storage.get(["local:devticket", ticket]);
|
|
27478
|
+
await storage.remove(["local:devticket", ticket]);
|
|
27479
|
+
if (!stored)
|
|
27480
|
+
throw new Error("Invalid or expired local dev ticket.");
|
|
27481
|
+
const email3 = readString(stored.email);
|
|
27482
|
+
if (!email3)
|
|
27483
|
+
throw new Error("Local dev ticket did not include an email.");
|
|
27484
|
+
return {
|
|
27485
|
+
subject: readString(stored.subject) ?? email3,
|
|
27486
|
+
email: email3,
|
|
27487
|
+
name: readString(stored.name),
|
|
27488
|
+
role: stored.role === "admin" || stored.role === "member" ? stored.role : undefined
|
|
27489
|
+
};
|
|
27490
|
+
}
|
|
27491
|
+
};
|
|
27492
|
+
}
|
|
27468
27493
|
function isBootstrapToken(value, config2) {
|
|
27469
27494
|
if (!value || !config2.bootstrapToken)
|
|
27470
27495
|
return false;
|
|
@@ -27502,6 +27527,17 @@ async function createLocalTicket(storage, user) {
|
|
|
27502
27527
|
}, new Date(Date.now() + 5 * 60 * 1000));
|
|
27503
27528
|
return ticket;
|
|
27504
27529
|
}
|
|
27530
|
+
var DEV_SESSION_EMAIL = "dev@kifaru.local";
|
|
27531
|
+
async function createDevLocalTicket(storage, user) {
|
|
27532
|
+
const ticket = `loc_${randomUUID2()}`;
|
|
27533
|
+
await storage.set(["local:devticket", ticket], {
|
|
27534
|
+
subject: `local:${user.email}`,
|
|
27535
|
+
email: user.email,
|
|
27536
|
+
name: user.name,
|
|
27537
|
+
role: user.role
|
|
27538
|
+
}, new Date(Date.now() + 60 * 1000));
|
|
27539
|
+
return ticket;
|
|
27540
|
+
}
|
|
27505
27541
|
async function readLocalAuthPayload(request) {
|
|
27506
27542
|
const contentType = request.headers.get("content-type") ?? "";
|
|
27507
27543
|
if (contentType.includes("application/json")) {
|
|
@@ -31588,6 +31624,15 @@ function requireHumanAuth(config2, c) {
|
|
|
31588
31624
|
}
|
|
31589
31625
|
return null;
|
|
31590
31626
|
}
|
|
31627
|
+
function isLoopbackPeer(c) {
|
|
31628
|
+
const server = c.env;
|
|
31629
|
+
try {
|
|
31630
|
+
const address = server?.requestIP?.(c.req.raw)?.address ?? "";
|
|
31631
|
+
return address === "127.0.0.1" || address === "::1" || address === "::ffff:127.0.0.1";
|
|
31632
|
+
} catch {
|
|
31633
|
+
return false;
|
|
31634
|
+
}
|
|
31635
|
+
}
|
|
31591
31636
|
function logGateway(level, category, message2) {
|
|
31592
31637
|
const line = `${new Date().toISOString()} [${level}] [${category}] ${message2}`;
|
|
31593
31638
|
console.log(line);
|
|
@@ -31675,28 +31720,70 @@ function createApp(input2) {
|
|
|
31675
31720
|
bootstrapToken: input2.config.hasBootstrapToken,
|
|
31676
31721
|
hasInternalToken: Boolean(input2.config.platformInternalToken)
|
|
31677
31722
|
}));
|
|
31678
|
-
app.get("/api/v1/auth/dev-token", (c) => {
|
|
31679
|
-
|
|
31680
|
-
|
|
31681
|
-
|
|
31723
|
+
app.get("/api/v1/auth/dev-token", async (c) => {
|
|
31724
|
+
if (input2.config.hasOAuthProviders) {
|
|
31725
|
+
return c.json({ error: "OAuth sign-in is configured for this deployment." }, 403);
|
|
31726
|
+
}
|
|
31727
|
+
if (!isLoopbackPeer(c)) {
|
|
31682
31728
|
return c.json({ error: "Forbidden" }, 403);
|
|
31683
31729
|
}
|
|
31684
|
-
|
|
31685
|
-
if (
|
|
31686
|
-
|
|
31687
|
-
|
|
31688
|
-
|
|
31730
|
+
let user = await getLocalUser(input2.storage, DEV_SESSION_EMAIL);
|
|
31731
|
+
if (!user) {
|
|
31732
|
+
user = await createLocalUser(input2.storage, {
|
|
31733
|
+
email: DEV_SESSION_EMAIL,
|
|
31734
|
+
password: `${randomUUID5()}${randomUUID5()}`,
|
|
31735
|
+
name: "Local Dev Session",
|
|
31736
|
+
role: "admin"
|
|
31737
|
+
});
|
|
31738
|
+
}
|
|
31739
|
+
const ticket = await createDevLocalTicket(input2.storage, user);
|
|
31740
|
+
const origin = input2.config.publicOrigin ?? publicOrigin(c.req.raw);
|
|
31741
|
+
const tokenResponse = await authFetch(new Request(`${origin}/token`, {
|
|
31742
|
+
method: "POST",
|
|
31743
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
31744
|
+
body: new URLSearchParams({
|
|
31745
|
+
grant_type: "client_credentials",
|
|
31746
|
+
provider: "devlocal",
|
|
31747
|
+
client_id: "kifaru-platform",
|
|
31748
|
+
client_secret: ticket
|
|
31749
|
+
}).toString()
|
|
31750
|
+
}));
|
|
31751
|
+
const tokenData = await tokenResponse.json().catch(() => ({}));
|
|
31752
|
+
const accessToken = readString(tokenData.access_token);
|
|
31753
|
+
if (!tokenResponse.ok || !accessToken) {
|
|
31754
|
+
return c.json({ error: "token_issuance_failed", detail: "Failed to issue session token." }, 500);
|
|
31755
|
+
}
|
|
31756
|
+
const csrfToken = randomUUID5();
|
|
31757
|
+
const isHttps = new URL(c.req.raw.url).protocol === "https:";
|
|
31758
|
+
const expiresIn = typeof tokenData.expires_in === "number" ? tokenData.expires_in : input2.config.accessTtlSeconds;
|
|
31759
|
+
setCookie(c, "kifaru_access", accessToken, {
|
|
31760
|
+
httpOnly: true,
|
|
31761
|
+
secure: isHttps,
|
|
31762
|
+
sameSite: "Lax",
|
|
31763
|
+
path: "/",
|
|
31764
|
+
maxAge: expiresIn
|
|
31765
|
+
});
|
|
31766
|
+
if (readString(tokenData.refresh_token)) {
|
|
31767
|
+
setCookie(c, "kifaru_refresh", String(tokenData.refresh_token), {
|
|
31768
|
+
httpOnly: true,
|
|
31769
|
+
secure: isHttps,
|
|
31689
31770
|
sameSite: "Lax",
|
|
31690
|
-
path: "/",
|
|
31691
|
-
maxAge:
|
|
31771
|
+
path: "/api/v1/auth",
|
|
31772
|
+
maxAge: Math.min(input2.config.refreshTtlSeconds, 30 * 86400)
|
|
31692
31773
|
});
|
|
31693
31774
|
}
|
|
31775
|
+
setCookie(c, "kifaru_csrf", csrfToken, {
|
|
31776
|
+
httpOnly: false,
|
|
31777
|
+
secure: isHttps,
|
|
31778
|
+
sameSite: "Lax",
|
|
31779
|
+
path: "/",
|
|
31780
|
+
maxAge: expiresIn
|
|
31781
|
+
});
|
|
31694
31782
|
return c.json({
|
|
31695
|
-
|
|
31696
|
-
|
|
31697
|
-
|
|
31698
|
-
|
|
31699
|
-
}
|
|
31783
|
+
ok: true,
|
|
31784
|
+
csrfToken,
|
|
31785
|
+
expiresInSeconds: expiresIn,
|
|
31786
|
+
user: { email: user.email, name: user.name, role: user.role }
|
|
31700
31787
|
});
|
|
31701
31788
|
});
|
|
31702
31789
|
if (input2.config.staticRoot && existsSync2(input2.config.staticRoot)) {
|
|
@@ -33075,6 +33162,7 @@ import {
|
|
|
33075
33162
|
defineTool,
|
|
33076
33163
|
getAgentDir
|
|
33077
33164
|
} from "@earendil-works/pi-coding-agent";
|
|
33165
|
+
import path6 from "path";
|
|
33078
33166
|
// ../core/src/model/model-family.ts
|
|
33079
33167
|
function getModelFamily(model) {
|
|
33080
33168
|
const ref = typeof model === "string" ? model : `${model.providerID}/${model.modelID}`;
|
|
@@ -68857,6 +68945,9 @@ class WorkbenchAgentService {
|
|
|
68857
68945
|
return this.adoptRuntime(id, runtime, actor, modelRef ?? null);
|
|
68858
68946
|
}
|
|
68859
68947
|
async resumeConversation(actor, sessionPath, modelRef) {
|
|
68948
|
+
if (!isSharedSessionPath(sessionPath)) {
|
|
68949
|
+
throw new Error("Session path is outside the shared session store.");
|
|
68950
|
+
}
|
|
68860
68951
|
const id = `wb-${crypto.randomUUID().slice(0, 8)}`;
|
|
68861
68952
|
const sessionManager = SessionManager.open(sessionPath, undefined, this.cwd);
|
|
68862
68953
|
const runtime = await createAgentSessionRuntime(this.makeRuntimeFactory(id, modelRef ?? ""), {
|
|
@@ -68877,8 +68968,8 @@ class WorkbenchAgentService {
|
|
|
68877
68968
|
tools: new Map,
|
|
68878
68969
|
findingCount: 0,
|
|
68879
68970
|
revision: 0,
|
|
68880
|
-
dirty: true,
|
|
68881
68971
|
runID: null,
|
|
68972
|
+
runCycle: 0,
|
|
68882
68973
|
runPending: null,
|
|
68883
68974
|
actor
|
|
68884
68975
|
};
|
|
@@ -68890,6 +68981,9 @@ class WorkbenchAgentService {
|
|
|
68890
68981
|
getConversation(id) {
|
|
68891
68982
|
return this.conversations.get(id);
|
|
68892
68983
|
}
|
|
68984
|
+
canAccess(conversation, actor) {
|
|
68985
|
+
return conversation.actor.workspaceID === actor.workspaceID;
|
|
68986
|
+
}
|
|
68893
68987
|
refreshTitle(conversation) {
|
|
68894
68988
|
const firstUser = conversation.session.messages.find((message2) => message2.role === "user");
|
|
68895
68989
|
const text2 = firstUser && typeof firstUser.content === "string" ? firstUser.content : firstUser && Array.isArray(firstUser.content) ? firstUser.content.map((block) => ("text" in block) ? String(block.text) : "").join(" ").trim() : "";
|
|
@@ -68905,7 +68999,6 @@ class WorkbenchAgentService {
|
|
|
68905
68999
|
status: "running",
|
|
68906
69000
|
startedAt: Date.now()
|
|
68907
69001
|
});
|
|
68908
|
-
conversation.dirty = true;
|
|
68909
69002
|
break;
|
|
68910
69003
|
case "tool_execution_end": {
|
|
68911
69004
|
const existing = conversation.tools.get(event.toolCallId);
|
|
@@ -68916,24 +69009,20 @@ class WorkbenchAgentService {
|
|
|
68916
69009
|
startedAt: existing?.startedAt ?? Date.now(),
|
|
68917
69010
|
durationMs: Date.now() - (existing?.startedAt ?? Date.now())
|
|
68918
69011
|
});
|
|
68919
|
-
conversation.dirty = true;
|
|
68920
69012
|
break;
|
|
68921
69013
|
}
|
|
68922
69014
|
case "agent_start":
|
|
68923
69015
|
this.ensurePlatformRun(conversation);
|
|
68924
|
-
conversation.dirty = true;
|
|
68925
69016
|
break;
|
|
68926
69017
|
case "agent_end":
|
|
68927
69018
|
this.completePlatformRun(conversation);
|
|
68928
69019
|
this.refreshTitle(conversation);
|
|
68929
|
-
conversation.dirty = true;
|
|
68930
69020
|
break;
|
|
68931
69021
|
case "message_start":
|
|
68932
69022
|
case "message_update":
|
|
68933
69023
|
case "message_end":
|
|
68934
69024
|
case "turn_start":
|
|
68935
69025
|
case "turn_end":
|
|
68936
|
-
conversation.dirty = true;
|
|
68937
69026
|
break;
|
|
68938
69027
|
default:
|
|
68939
69028
|
break;
|
|
@@ -68948,7 +69037,6 @@ class WorkbenchAgentService {
|
|
|
68948
69037
|
...conversation.session.isStreaming ? { deliverAs: "steer" } : {}
|
|
68949
69038
|
});
|
|
68950
69039
|
this.refreshTitle(conversation);
|
|
68951
|
-
conversation.dirty = true;
|
|
68952
69040
|
conversation.revision += 1;
|
|
68953
69041
|
}
|
|
68954
69042
|
snapshot(conversation) {
|
|
@@ -68973,7 +69061,6 @@ class WorkbenchAgentService {
|
|
|
68973
69061
|
if (model && !conversation.session.isStreaming) {
|
|
68974
69062
|
await conversation.session.setModel?.(model);
|
|
68975
69063
|
}
|
|
68976
|
-
conversation.dirty = true;
|
|
68977
69064
|
conversation.revision += 1;
|
|
68978
69065
|
}
|
|
68979
69066
|
async ensurePlatformRun(conversation) {
|
|
@@ -68988,7 +69075,7 @@ class WorkbenchAgentService {
|
|
|
68988
69075
|
kind: "hunt",
|
|
68989
69076
|
title: `Live workspace \xB7 ${conversation.title}`.slice(0, 240),
|
|
68990
69077
|
configuration: { surface: "workbench", cwd: this.cwd },
|
|
68991
|
-
idempotencyKey: `workbench-${conversation.id}`
|
|
69078
|
+
idempotencyKey: `workbench-${conversation.id}-${conversation.runCycle}`
|
|
68992
69079
|
});
|
|
68993
69080
|
conversation.runID = run.id;
|
|
68994
69081
|
this.platform.transitionRun(conversation.actor.workspaceID, run.id, "running");
|
|
@@ -69003,10 +69090,13 @@ class WorkbenchAgentService {
|
|
|
69003
69090
|
}
|
|
69004
69091
|
}
|
|
69005
69092
|
completePlatformRun(conversation) {
|
|
69006
|
-
|
|
69093
|
+
const runID = conversation.runID;
|
|
69094
|
+
if (!runID)
|
|
69007
69095
|
return;
|
|
69096
|
+
conversation.runID = null;
|
|
69097
|
+
conversation.runCycle += 1;
|
|
69008
69098
|
try {
|
|
69009
|
-
this.platform.transitionRun(conversation.actor.workspaceID,
|
|
69099
|
+
this.platform.transitionRun(conversation.actor.workspaceID, runID, "completed", {
|
|
69010
69100
|
summary: { findings: conversation.findingCount, surface: "workbench" }
|
|
69011
69101
|
});
|
|
69012
69102
|
} catch (error92) {
|
|
@@ -69031,7 +69121,7 @@ class WorkbenchAgentService {
|
|
|
69031
69121
|
}
|
|
69032
69122
|
const p = params;
|
|
69033
69123
|
const finding = {
|
|
69034
|
-
fingerprint: (p.fingerprint?.trim() || `workbench:${slugify2(p.title)}`).slice(0, 512),
|
|
69124
|
+
fingerprint: (p.fingerprint?.trim() || `workbench:${slugify2(p.asset)}:${slugify2(p.title)}`).slice(0, 512),
|
|
69035
69125
|
ruleID: (p.weakness?.trim() || slugify2(p.title) || "finding").slice(0, 160),
|
|
69036
69126
|
title: p.title.slice(0, 256),
|
|
69037
69127
|
...p.description ? { description: p.description.slice(0, 20000) } : {},
|
|
@@ -69073,6 +69163,11 @@ class WorkbenchAgentService {
|
|
|
69073
69163
|
});
|
|
69074
69164
|
}
|
|
69075
69165
|
}
|
|
69166
|
+
function isSharedSessionPath(sessionPath) {
|
|
69167
|
+
const resolved = path6.resolve(sessionPath);
|
|
69168
|
+
const root = path6.resolve(getAgentDir(), "sessions");
|
|
69169
|
+
return resolved.endsWith(".jsonl") && resolved.startsWith(root + path6.sep);
|
|
69170
|
+
}
|
|
69076
69171
|
function slugify2(value) {
|
|
69077
69172
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
69078
69173
|
}
|
|
@@ -69104,6 +69199,7 @@ var reportFindingSchema = exports_typebox.Object({
|
|
|
69104
69199
|
|
|
69105
69200
|
// src/workbench/ws.ts
|
|
69106
69201
|
var SNAPSHOT_INTERVAL_MS = 150;
|
|
69202
|
+
var FOREIGN_WORKSPACE_NOTICE = "That conversation belongs to another workspace.";
|
|
69107
69203
|
function workbenchSocketHandlers(service) {
|
|
69108
69204
|
function send(socket, message2) {
|
|
69109
69205
|
try {
|
|
@@ -69115,17 +69211,33 @@ function workbenchSocketHandlers(service) {
|
|
|
69115
69211
|
clearInterval(data.timer);
|
|
69116
69212
|
data.timer = setInterval(() => {
|
|
69117
69213
|
const conversation = data.conversationId ? service.getConversation(data.conversationId) : undefined;
|
|
69118
|
-
if (!conversation
|
|
69214
|
+
if (!conversation)
|
|
69215
|
+
return;
|
|
69216
|
+
const revision = conversation.revision;
|
|
69217
|
+
if (revision <= (data.lastRevision ?? 0))
|
|
69119
69218
|
return;
|
|
69120
|
-
|
|
69219
|
+
data.lastRevision = revision;
|
|
69121
69220
|
send(socket, service.snapshot(conversation));
|
|
69122
69221
|
}, SNAPSHOT_INTERVAL_MS);
|
|
69123
69222
|
}
|
|
69124
69223
|
function attach(socket, data, conversation) {
|
|
69125
69224
|
data.conversationId = conversation.id;
|
|
69225
|
+
data.lastRevision = conversation.revision;
|
|
69126
69226
|
send(socket, service.snapshot(conversation));
|
|
69127
69227
|
pump(socket, data);
|
|
69128
69228
|
}
|
|
69229
|
+
function governedConversation(data, onForeign, onMissing) {
|
|
69230
|
+
const conversation = data.conversationId ? service.getConversation(data.conversationId) : undefined;
|
|
69231
|
+
if (!conversation) {
|
|
69232
|
+
onMissing();
|
|
69233
|
+
return;
|
|
69234
|
+
}
|
|
69235
|
+
if (!service.canAccess(conversation, data.actor)) {
|
|
69236
|
+
onForeign();
|
|
69237
|
+
return;
|
|
69238
|
+
}
|
|
69239
|
+
return conversation;
|
|
69240
|
+
}
|
|
69129
69241
|
return {
|
|
69130
69242
|
open() {},
|
|
69131
69243
|
async message(socket, raw2) {
|
|
@@ -69141,6 +69253,10 @@ function workbenchSocketHandlers(service) {
|
|
|
69141
69253
|
switch (message2.type) {
|
|
69142
69254
|
case "attach": {
|
|
69143
69255
|
const existing = message2.conversationId ? service.getConversation(message2.conversationId) : undefined;
|
|
69256
|
+
if (existing && !service.canAccess(existing, data.actor)) {
|
|
69257
|
+
send(socket, { type: "notice", level: "error", text: FOREIGN_WORKSPACE_NOTICE });
|
|
69258
|
+
return;
|
|
69259
|
+
}
|
|
69144
69260
|
const conversation = existing ?? await service.createConversation(data.actor);
|
|
69145
69261
|
attach(socket, data, conversation);
|
|
69146
69262
|
send(socket, { type: "models", models: service.listModels() });
|
|
@@ -69162,16 +69278,14 @@ function workbenchSocketHandlers(service) {
|
|
|
69162
69278
|
}
|
|
69163
69279
|
case "prompt":
|
|
69164
69280
|
case "steer": {
|
|
69165
|
-
const conversation = data
|
|
69166
|
-
if (!conversation)
|
|
69167
|
-
send(socket, { type: "notice", level: "error", text: "No conversation attached" });
|
|
69281
|
+
const conversation = governedConversation(data, () => send(socket, { type: "notice", level: "error", text: FOREIGN_WORKSPACE_NOTICE }), () => send(socket, { type: "notice", level: "error", text: "No conversation attached" }));
|
|
69282
|
+
if (!conversation)
|
|
69168
69283
|
return;
|
|
69169
|
-
}
|
|
69170
69284
|
await service.prompt(conversation, message2.text);
|
|
69171
69285
|
break;
|
|
69172
69286
|
}
|
|
69173
69287
|
case "select_model": {
|
|
69174
|
-
const conversation = data
|
|
69288
|
+
const conversation = governedConversation(data, () => send(socket, { type: "notice", level: "error", text: FOREIGN_WORKSPACE_NOTICE }), () => {});
|
|
69175
69289
|
if (conversation)
|
|
69176
69290
|
await service.selectModel(conversation, message2.model);
|
|
69177
69291
|
break;
|
package/kifaru-pi.js
CHANGED
|
@@ -16476,7 +16476,7 @@ var init_installation = __esm(async () => {
|
|
|
16476
16476
|
});
|
|
16477
16477
|
}
|
|
16478
16478
|
Installation.upgrade = upgrade;
|
|
16479
|
-
Installation.VERSION = "1.0.
|
|
16479
|
+
Installation.VERSION = "1.0.178";
|
|
16480
16480
|
Installation.CHANNEL = typeof KIFARU_CHANNEL === "string" ? KIFARU_CHANNEL : "local";
|
|
16481
16481
|
Installation.USER_AGENT = `kifaru/${Installation.CHANNEL}/${Installation.VERSION}`;
|
|
16482
16482
|
async function latest() {
|
|
@@ -133185,7 +133185,7 @@ Ctrl+C stops the gateway.`);
|
|
|
133185
133185
|
async function handleCli() {
|
|
133186
133186
|
const args = process.argv.slice(2);
|
|
133187
133187
|
if (args.includes("--version") || args.includes("-v")) {
|
|
133188
|
-
console.log("1.0.
|
|
133188
|
+
console.log("1.0.178");
|
|
133189
133189
|
process.exit(0);
|
|
133190
133190
|
}
|
|
133191
133191
|
if (args.includes("--help") || args.includes("-h")) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kifaru",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"gitHead": "
|
|
3
|
+
"version": "1.0.178",
|
|
4
|
+
"gitHead": "6918585a066d299c117d06522515ad010e37e688",
|
|
5
5
|
"description": "Kifaru security agent on the Pi runtime",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|