Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/fix-kiloclaw-slash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@kilocode/cli": patch
"@kilocode/kilo-gateway": patch
"kilo-code": patch
---

Fix opening KiloClaw from the CLI and VS Code slash commands.
1 change: 1 addition & 0 deletions packages/kilo-gateway/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export {
getNotifications,
getProfile,
getToken,
normalizeClawStatus,
setOrganization,
} from "./server/handlers.js"

Expand Down
18 changes: 17 additions & 1 deletion packages/kilo-gateway/src/server/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,23 @@ export async function getClawStatus(auth: AuthStore) {

const response = await fetch(`${KILO_API_BASE}/api/kiloclaw/status`, { headers })
if (!response.ok) throw new GatewayError(await response.text(), response.status)
return response.json()
return normalizeClawStatus(await response.json())
}

function normalizeTime(value: unknown) {
if (typeof value === "number" && Number.isFinite(value)) return new Date(value).toISOString()
return value
}

export function normalizeClawStatus(input: unknown) {
if (!input || typeof input !== "object" || Array.isArray(input)) return input

const data = input as Record<string, unknown>
return {
...data,
...("lastStartedAt" in data ? { lastStartedAt: normalizeTime(data.lastStartedAt) } : {}),
...("lastStoppedAt" in data ? { lastStoppedAt: normalizeTime(data.lastStoppedAt) } : {}),
}
}

export async function getClawChatCredentials(auth: AuthStore): Promise<ClawChatCredentials> {
Expand Down
3 changes: 3 additions & 0 deletions packages/kilo-vscode/src/KiloProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,9 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
case "openSettingsPanel":
vscode.commands.executeCommand("kilo-code.new.settingsButtonClicked", message.tab)
break
case "openKiloClaw":
vscode.commands.executeCommand("kilo-code.new.kiloClawOpen")
break
case "openVSCodeSettings":
vscode.commands.executeCommand("workbench.action.openSettings", message.query)
break
Expand Down
8 changes: 8 additions & 0 deletions packages/kilo-vscode/webview-ui/src/hooks/useSlashCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,14 @@ export function useSlashCommand(
vscode.postMessage({ type: "toggleRemote" })
},
},
{
name: "kiloclaw",
description: "Open KiloClaw chat",
hints: ["claw"],
action: () => {
vscode.postMessage({ type: "openKiloClaw" })
},
},
{
name: "sandbox",
description: "Toggle sandbox",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ export interface OpenAdvancedWorktreeRequest {
type: "openAdvancedWorktree"
}

export interface OpenKiloClawRequest {
type: "openKiloClaw"
}

export interface RequestAgentsMessage {
type: "requestAgents"
}
Expand Down Expand Up @@ -1172,6 +1176,7 @@ export type WebviewMessage =
| OpenMarketplacePanelRequest
| OpenAgentManagerRequest
| OpenAdvancedWorktreeRequest
| OpenKiloClawRequest
| OpenFileRequest
| ValidateFilesRequest
| CancelLoginRequest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
getOrganizationId,
getToken,
importSessionToDb,
normalizeClawStatus,
} from "@kilocode/kilo-gateway"
import {
HEADER_FEATURE,
Expand Down Expand Up @@ -360,7 +361,7 @@ export const kiloGatewayHandlers = HttpApiBuilder.group(InstanceHttpApi, "kilo",
try: async () => {
const response = await fetch(`${KILO_API_BASE}/api/kiloclaw/status`, { headers })
if (!response.ok) throw new GatewayError(await response.text(), response.status)
return Schema.decodeUnknownPromise(ClawStatus)(await response.json())
return Schema.decodeUnknownPromise(ClawStatus)(normalizeClawStatus(await response.json()))
},
catch: (err) => err,
}).pipe(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,32 @@ describe("Kilo gateway HttpApi statuses", () => {
}),
)

it.live("normalizes numeric KiloClaw timestamps", () =>
Effect.gen(function* () {
const started = 1_700_000_000_000
yield* stub(() =>
Response.json({
status: "running",
sandboxId: "sandbox",
userId: "user",
lastStartedAt: started,
lastStoppedAt: null,
}),
)

const response = yield* HttpClient.get(KiloGatewayPaths.clawStatus)

expect(response.status).toBe(200)
expect(yield* response.json).toEqual({
status: "running",
sandboxId: "sandbox",
userId: "user",
lastStartedAt: new Date(started).toISOString(),
lastStoppedAt: null,
})
}),
)

it.live("maps KiloClaw transport failures to bad gateway", () =>
Effect.gen(function* () {
yield* stub(() => Promise.reject(new TypeError("network error")))
Expand Down
Loading