Skip to content
Open
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
25 changes: 1 addition & 24 deletions apps/web/src/actions/opencode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -826,13 +826,6 @@ export async function sendMessageAction(
message?: WorkspaceMessage;
error?: string;
}> {
console.log("[sendMessageAction] Called with:", {
slug,
sessionId,
text: text.substring(0, 50),
model,
});

// Verify user is authorized
const session = await getSession();
if (!session) {
Expand All @@ -851,11 +844,6 @@ export async function sendMessageAction(
!instance.serverPassword ||
instance.status !== "running"
) {
console.log("[sendMessageAction] Instance unavailable:", {
hasInstance: !!instance,
hasPassword: !!instance?.serverPassword,
status: instance?.status,
});
return { ok: false, error: "instance_unavailable" };
}

Expand All @@ -865,11 +853,6 @@ export async function sendMessageAction(
)}`;
const baseUrl = getInstanceUrl(slug);

console.log(
"[sendMessageAction] Sending to:",
`${baseUrl}/session/${sessionId}/message`
);

const body = {
parts: [{ type: "text", text }],
model: model
Expand Down Expand Up @@ -912,14 +895,8 @@ export async function sendMessageAction(
}
}
}

console.log(
"[sendMessageAction] Extracted text:",
textContent.substring(0, 100)
);
} catch {
// If not valid JSON, maybe it's streaming format (NDJSON)
console.log("[sendMessageAction] JSON parse failed, trying NDJSON");
// If not valid JSON, fall back to NDJSON parsing.
const lines = responseText.split("\n");

for (const line of lines) {
Expand Down
9 changes: 0 additions & 9 deletions apps/web/src/actions/spawner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,29 +85,22 @@ export async function ensureInstanceRunningAction(slug: string): Promise<{
status: 'running' | 'starting' | 'error'
error?: string
}> {
console.log('[ensureInstanceRunning] Starting for slug:', slug)

try {
const session = await getSession()
if (!session) {
console.log('[ensureInstanceRunning] No session - unauthorized')
return { status: 'error', error: 'unauthorized' }
}
console.log('[ensureInstanceRunning] Session user:', session.user.slug, 'role:', session.user.role)

if (session.user.slug !== slug && session.user.role !== 'ADMIN') {
console.log('[ensureInstanceRunning] Forbidden - slug mismatch')
return { status: 'error', error: 'forbidden' }
}

const kickstartStatus = await getKickstartStatus()
if (kickstartStatus !== 'ready') {
console.log('[ensureInstanceRunning] Kickstart setup required')
return { status: 'error', error: 'setup_required' }
}

const instance = await getWorkspaceStatus(slug)
console.log('[ensureInstanceRunning] Current instance status:', instance?.status ?? 'none')

if (instance?.status === 'running') {
const syncUserId =
Expand Down Expand Up @@ -146,9 +139,7 @@ export async function ensureInstanceRunningAction(slug: string): Promise<{
return { status: 'starting' }
}

console.log('[ensureInstanceRunning] Starting instance...')
const result = await startWorkspace(slug, session.user.id)
console.log('[ensureInstanceRunning] Start result:', result)

if (!result.ok) {
return { status: 'error', error: result.detail ?? result.error }
Expand Down
14 changes: 12 additions & 2 deletions apps/web/src/lib/opencode/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import { WORKSPACE_ATTACHMENTS_DIR } from "@/lib/workspace-attachments";
*/
const HIDDEN_PART_TYPES = new Set(["snapshot", "compaction"]);

// Track which unknown part types we have already logged so each new kind only
// fires once per process. Without this, a single unrecognized part type would
// log the full object on every streamed message and rapidly fill disk.
const loggedUnknownPartTypes = new Set<string>();

function resolveFilePartPath(sourcePath: string | undefined, fileUrl: string | undefined): string | undefined {
if (sourcePath) {
return sourcePath;
Expand Down Expand Up @@ -238,8 +243,13 @@ export function transformParts(parts: unknown[]): MessagePart[] {
}

default: {
// Unknown type - preserve as fallback for debugging
console.log("[transformParts] Unknown part type:", partType, part);
// Unknown type - preserve as fallback for debugging.
// Log only once per kind to avoid flooding logs during streaming.
const kind = typeof partType === "string" ? partType : "non-string";
if (!loggedUnknownPartTypes.has(kind)) {
loggedUnknownPartTypes.add(kind);
console.warn("[transformParts] Unknown part type:", kind);
}
const normalizedData = normalizeSerializableValue(part);
return {
type: "unknown" as const,
Expand Down
7 changes: 7 additions & 0 deletions apps/web/src/lib/spawner/__tests__/docker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@ describe('docker', () => {
'/opt/arche/kb-content:/kb-content',
'/opt/arche/users/user-slug:/tmp/arche-user-data:ro',
],
LogConfig: {
Type: 'json-file',
Config: {
'max-size': '10m',
'max-file': '3',
},
},
},
Labels: {
'arche.managed': 'true',
Expand Down
8 changes: 0 additions & 8 deletions apps/web/src/lib/spawner/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,20 +283,12 @@ async function waitForHealthy(containerId: string, slug: string, password: strin

if (directBaseUrl === undefined) {
directBaseUrl = await getContainerHealthBaseUrl(containerId)
if (directBaseUrl) {
console.log('[spawner] Using direct container IP for initial healthcheck', {
baseUrl: directBaseUrl,
containerId,
slug,
})
}
}

if (directBaseUrl && !directHealthy) {
const directHealth = await isInstanceHealthyWithPassword(slug, password, directBaseUrl)
if (directHealth.ok) {
directHealthy = true
console.log('[spawner] OpenCode responded on direct container IP', { containerId, slug })
} else {
lastHealth = directHealth
}
Expand Down
10 changes: 10 additions & 0 deletions apps/web/src/lib/spawner/docker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,16 @@ export async function createContainer(
NetworkMode: getOpencodeNetwork(),
RestartPolicy: { Name: "on-failure", MaximumRetryCount: 5 },
Binds: binds,
// Cap per-workspace container logs to avoid filling host disk.
// Podman aliases json-file to k8s-file; max-size is honored, max-file
// is best-effort (some Podman versions truncate instead of rotating).
LogConfig: {
Type: "json-file",
Config: {
"max-size": "10m",
"max-file": "3",
},
},
},
Labels: {
"arche.managed": "true",
Expand Down
2 changes: 2 additions & 0 deletions infra/deploy/ansible/roles/app/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,8 @@
--label arche.role=web \
--label arche.version={{ web_version }} \
--restart on-failure:5 \
--log-driver k8s-file \
--log-opt max-size=10m \
{{ web_image }}
changed_when: true
no_log: true
Expand Down
13 changes: 13 additions & 0 deletions infra/deploy/ansible/roles/app/templates/compose.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,17 @@

name: arche

x-logging: &default-logging
driver: "json-file"
options:
max-size: "10m"
max-file: "3"

services:
traefik:
image: docker.io/traefik:v3.6.7
restart: "on-failure:5"
logging: *default-logging
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
Expand Down Expand Up @@ -58,6 +65,7 @@ services:
cloudflared:
image: {{ cloudflared_image }}
restart: "on-failure:5"
logging: *default-logging
command:
- tunnel
- --no-autoupdate
Expand All @@ -73,6 +81,7 @@ services:
docker-socket-proxy:
image: ghcr.io/tecnativa/docker-socket-proxy:latest
restart: "on-failure:5"
logging: *default-logging
{% if deploy_mode == 'local-dev' %}
user: root
security_opt:
Expand All @@ -98,6 +107,7 @@ services:
postgres:
image: docker.io/postgres:16
restart: "on-failure:5"
logging: *default-logging
environment:
POSTGRES_DB: arche
POSTGRES_USER: postgres
Expand Down Expand Up @@ -142,6 +152,7 @@ services:
reaper:
image: {{ web_image }}
restart: "on-failure:5"
logging: *default-logging
command:
- pnpm
- run
Expand All @@ -164,6 +175,7 @@ services:
flows:
image: {{ web_image }}
restart: "unless-stopped"
logging: *default-logging
command:
- pnpm
- run
Expand Down Expand Up @@ -199,6 +211,7 @@ services:
image: node:24.14.0-bookworm-slim
working_dir: /app
restart: "unless-stopped"
logging: *default-logging
env_file:
- {{ env_file_name | default('.env') }}
ports:
Expand Down