@abitat_reece/host-daemon 0.1.9 → 0.1.10
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/index.js +16 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/local-control/codex-bridge.d.ts +73 -1
- package/dist/local-control/codex-bridge.d.ts.map +1 -1
- package/dist/local-control/codex-bridge.js +476 -59
- package/dist/local-control/codex-bridge.js.map +1 -1
- package/dist/local-control/diagnostics-log.d.ts +31 -0
- package/dist/local-control/diagnostics-log.d.ts.map +1 -0
- package/dist/local-control/diagnostics-log.js +96 -0
- package/dist/local-control/diagnostics-log.js.map +1 -0
- package/dist/local-control/push-notifications.d.ts +3 -0
- package/dist/local-control/push-notifications.d.ts.map +1 -1
- package/dist/local-control/push-notifications.js +69 -7
- package/dist/local-control/push-notifications.js.map +1 -1
- package/dist/local-control/relay-client.d.ts +4 -0
- package/dist/local-control/relay-client.d.ts.map +1 -1
- package/dist/local-control/relay-client.js +32 -4
- package/dist/local-control/relay-client.js.map +1 -1
- package/dist/local-control/server.d.ts +4 -0
- package/dist/local-control/server.d.ts.map +1 -1
- package/dist/local-control/server.js +250 -49
- package/dist/local-control/server.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import WebSocket from "ws";
|
|
2
2
|
import { createRelayReplayCache, decryptRelayEnvelope, encryptRelayEnvelope, relaySessionKey, validateRelayEnvelopeFreshness } from "@abitat_reece/shared";
|
|
3
|
+
import { errorDiagnostics, logDiagnostics } from "./diagnostics-log.js";
|
|
3
4
|
const RELAY_HEARTBEAT_INTERVAL_MS = 15_000;
|
|
4
5
|
const RELAY_HEARTBEAT_STALE_MS = 45_000;
|
|
5
6
|
export { createRelayReplayCache };
|
|
@@ -39,18 +40,28 @@ export function startRelayClient(input) {
|
|
|
39
40
|
return;
|
|
40
41
|
}
|
|
41
42
|
const url = relayWebSocketUrl(input.relayEndpoint, input.relayId);
|
|
43
|
+
logDiagnostics(input.diagnostics, "info", "relay.connect.attempt", {
|
|
44
|
+
relayEndpoint: input.relayEndpoint,
|
|
45
|
+
relayId: input.relayId
|
|
46
|
+
});
|
|
42
47
|
const activeSocket = new WebSocket(url);
|
|
43
48
|
socket = activeSocket;
|
|
44
49
|
activeSocket.on("open", () => {
|
|
45
50
|
if (socket !== activeSocket || stopped) {
|
|
46
51
|
return;
|
|
47
52
|
}
|
|
53
|
+
logDiagnostics(input.diagnostics, "info", "relay.connect.open", {
|
|
54
|
+
relayEndpoint: input.relayEndpoint,
|
|
55
|
+
relayId: input.relayId
|
|
56
|
+
});
|
|
48
57
|
heartbeat?.stop();
|
|
49
58
|
heartbeat = createRelayHeartbeatController({
|
|
59
|
+
diagnostics: input.diagnostics,
|
|
60
|
+
diagnosticsMetadata: { relayId: input.relayId },
|
|
50
61
|
heartbeatIntervalMs: input.heartbeatIntervalMs,
|
|
51
62
|
onStale: () => {
|
|
52
63
|
if (socket === activeSocket) {
|
|
53
|
-
scheduleReconnect();
|
|
64
|
+
scheduleReconnect("stale_heartbeat");
|
|
54
65
|
}
|
|
55
66
|
},
|
|
56
67
|
socket: activeSocket,
|
|
@@ -79,19 +90,31 @@ export function startRelayClient(input) {
|
|
|
79
90
|
if (socket !== activeSocket) {
|
|
80
91
|
return;
|
|
81
92
|
}
|
|
93
|
+
logDiagnostics(input.diagnostics, "warn", "relay.connect.closed", {
|
|
94
|
+
relayId: input.relayId
|
|
95
|
+
});
|
|
82
96
|
heartbeat?.stop();
|
|
83
97
|
heartbeat = null;
|
|
84
98
|
socket = null;
|
|
85
|
-
scheduleReconnect();
|
|
99
|
+
scheduleReconnect("socket_closed");
|
|
86
100
|
});
|
|
87
|
-
activeSocket.on("error", () => {
|
|
101
|
+
activeSocket.on("error", (error) => {
|
|
102
|
+
logDiagnostics(input.diagnostics, "warn", "relay.connect.error", {
|
|
103
|
+
error: errorDiagnostics(error),
|
|
104
|
+
relayId: input.relayId
|
|
105
|
+
});
|
|
88
106
|
activeSocket.close();
|
|
89
107
|
});
|
|
90
108
|
};
|
|
91
|
-
const scheduleReconnect = () => {
|
|
109
|
+
const scheduleReconnect = (reason) => {
|
|
92
110
|
if (stopped || reconnectTimer) {
|
|
93
111
|
return;
|
|
94
112
|
}
|
|
113
|
+
logDiagnostics(input.diagnostics, "info", "relay.reconnect.scheduled", {
|
|
114
|
+
delayMs: input.reconnectIntervalMs ?? 2_000,
|
|
115
|
+
reason,
|
|
116
|
+
relayId: input.relayId
|
|
117
|
+
});
|
|
95
118
|
reconnectTimer = setTimeout(() => {
|
|
96
119
|
reconnectTimer = null;
|
|
97
120
|
connect();
|
|
@@ -125,6 +148,11 @@ export function createRelayHeartbeatController(input) {
|
|
|
125
148
|
return;
|
|
126
149
|
}
|
|
127
150
|
if (now() - lastHeartbeatAckAt > staleAfterMs) {
|
|
151
|
+
logDiagnostics(input.diagnostics, "warn", "relay.heartbeat.stale", {
|
|
152
|
+
...input.diagnosticsMetadata,
|
|
153
|
+
msSinceLastAck: now() - lastHeartbeatAckAt,
|
|
154
|
+
staleAfterMs
|
|
155
|
+
});
|
|
128
156
|
stop();
|
|
129
157
|
if (typeof input.socket.terminate === "function") {
|
|
130
158
|
input.socket.terminate();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-client.js","sourceRoot":"","sources":["../../src/local-control/relay-client.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,IAAI,CAAC;AAE3B,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,8BAA8B,EAG/B,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"relay-client.js","sourceRoot":"","sources":["../../src/local-control/relay-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type IncomingMessage, type Server, type ServerResponse } from "node:http";
|
|
2
2
|
import type { CodexMobileModelSettings, CodexModelOption, ConversationStatus } from "@abitat_reece/shared";
|
|
3
3
|
import { type LocalControlStore, type LocalControlTransport } from "./state.js";
|
|
4
|
+
import { type MobileControlDiagnosticsLogger } from "./diagnostics-log.js";
|
|
4
5
|
export interface LocalCodexProjectSummary {
|
|
5
6
|
id: string;
|
|
6
7
|
workspaceId: string;
|
|
@@ -40,6 +41,7 @@ export interface LocalCodexMessage {
|
|
|
40
41
|
metadata?: Record<string, unknown>;
|
|
41
42
|
createdAt: string;
|
|
42
43
|
}
|
|
44
|
+
export type LocalCodexDeliveryMode = "queue" | "steer";
|
|
43
45
|
export interface LocalCodexCompletionState {
|
|
44
46
|
conversationId: string;
|
|
45
47
|
failed: boolean;
|
|
@@ -61,6 +63,7 @@ export interface LocalCodexBridge {
|
|
|
61
63
|
}>;
|
|
62
64
|
continueConversation(conversationId: string, input: {
|
|
63
65
|
attachments?: LocalAttachmentReference[];
|
|
66
|
+
delivery?: LocalCodexDeliveryMode;
|
|
64
67
|
modelSettings?: CodexMobileModelSettings;
|
|
65
68
|
prompt: string;
|
|
66
69
|
}): Promise<{
|
|
@@ -93,6 +96,7 @@ interface StartLocalControlServerInput {
|
|
|
93
96
|
attachmentDirectory?: string;
|
|
94
97
|
bindHost: string;
|
|
95
98
|
codex: LocalCodexBridge;
|
|
99
|
+
diagnostics?: MobileControlDiagnosticsLogger;
|
|
96
100
|
endpoint: string;
|
|
97
101
|
port: number;
|
|
98
102
|
store: LocalControlStore;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/local-control/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,MAAM,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAKjG,OAAO,KAAK,EACV,wBAAwB,EACxB,gBAAgB,EAChB,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAE3B,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/local-control/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,MAAM,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAKjG,OAAO,KAAK,EACV,wBAAwB,EACxB,gBAAgB,EAChB,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAE3B,MAAM,YAAY,CAAC;AACpB,OAAO,EAKL,KAAK,8BAA8B,EACpC,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,WAAW,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,6BAA6B;IAC5C,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,WAAW,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;IAClD,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,OAAO,CAAC;AAEvD,MAAM,WAAW,yBAAyB;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,oBAAoB,CAClB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE;QACL,WAAW,CAAC,EAAE,wBAAwB,EAAE,CAAC;QACzC,QAAQ,CAAC,EAAE,sBAAsB,CAAC;QAClC,aAAa,CAAC,EAAE,wBAAwB,CAAC;QACzC,MAAM,EAAE,MAAM,CAAC;KAChB,GACA,OAAO,CAAC;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5E,oBAAoB,IAAI,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAAC;IAC7D,YAAY,CACV,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,GAC7D,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAChC,gBAAgB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAChD,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,6BAA6B,EAAE,CAAC,CAAC;IACtF,YAAY,IAAI,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAAC;IACpD,iBAAiB,CACf,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;QACL,WAAW,CAAC,EAAE,wBAAwB,EAAE,CAAC;QACzC,aAAa,CAAC,EAAE,wBAAwB,CAAC;QACzC,MAAM,EAAE,MAAM,CAAC;KAChB,GACA,OAAO,CAAC;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7E;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,4BAA4B;IACpC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,gBAAgB,CAAC;IACxB,WAAW,CAAC,EAAE,8BAA8B,CAAC;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,iBAAiB,CAAC;IACzB,SAAS,EAAE,qBAAqB,CAAC;CAClC;AA0BD,wBAAsB,uBAAuB,CAAC,KAAK,EAAE,4BAA4B;;;;GAidhF"}
|
|
@@ -3,21 +3,25 @@ import { mkdir, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { defaultLocalAttachmentDirectory } from "./state.js";
|
|
6
|
+
import { attachmentDiagnostics, errorDiagnostics, logDiagnostics, promptDiagnostics } from "./diagnostics-log.js";
|
|
6
7
|
const LOCAL_WORKSPACE_ID = "local";
|
|
7
8
|
export async function startLocalControlServer(input) {
|
|
8
9
|
const identity = await input.store.getMacIdentity();
|
|
10
|
+
const diagnostics = input.diagnostics;
|
|
9
11
|
const sessions = new Map();
|
|
10
12
|
const signals = [];
|
|
11
13
|
let endpoint = input.endpoint;
|
|
12
14
|
const server = createServer(async (request, response) => {
|
|
15
|
+
const method = request.method ?? "GET";
|
|
16
|
+
let path = "/";
|
|
13
17
|
try {
|
|
14
|
-
if (
|
|
18
|
+
if (method === "OPTIONS") {
|
|
15
19
|
writeJson(response, 204, null);
|
|
16
20
|
return;
|
|
17
21
|
}
|
|
18
22
|
const url = new URL(request.url ?? "/", endpoint);
|
|
19
|
-
|
|
20
|
-
if (
|
|
23
|
+
path = normalizePath(url.pathname);
|
|
24
|
+
if (method === "GET" && path === "/health") {
|
|
21
25
|
const codex = await input.codex.bootstrap().catch((error) => ({
|
|
22
26
|
available: false,
|
|
23
27
|
error: errorMessage(error)
|
|
@@ -33,21 +37,59 @@ export async function startLocalControlServer(input) {
|
|
|
33
37
|
});
|
|
34
38
|
return;
|
|
35
39
|
}
|
|
36
|
-
if (
|
|
40
|
+
if (method === "POST" &&
|
|
37
41
|
(path === "/pairing/consume" || path === "/api/mobile/pairing/complete")) {
|
|
38
42
|
const body = await readJson(request);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
let pairing;
|
|
44
|
+
try {
|
|
45
|
+
pairing = await input.store.consumePairing({
|
|
46
|
+
code: stringValue(body.code),
|
|
47
|
+
deviceName: stringValue(body.deviceName) || "iPhone",
|
|
48
|
+
manualCode: stringValue(body.manualCode),
|
|
49
|
+
pairingSecret: stringValue(body.pairingSecret),
|
|
50
|
+
platform: stringValue(body.platform) || "ios"
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
const message = errorMessage(error);
|
|
55
|
+
logDiagnostics(diagnostics, "warn", message.includes("expired") ? "pairing.expired" : "pairing.rejected", {
|
|
56
|
+
error: message,
|
|
57
|
+
hasManualCode: Boolean(stringValue(body.manualCode) || stringValue(body.code)),
|
|
58
|
+
hasPairingSecret: Boolean(stringValue(body.pairingSecret)),
|
|
59
|
+
method,
|
|
60
|
+
path,
|
|
61
|
+
platform: stringValue(body.platform) || "ios"
|
|
62
|
+
});
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
logDiagnostics(diagnostics, "info", "pairing.consumed", {
|
|
66
|
+
deviceId: pairing.machineId,
|
|
67
|
+
hostMachineId: pairing.hostMachineId,
|
|
68
|
+
method,
|
|
69
|
+
path,
|
|
70
|
+
platform: stringValue(body.platform) || "ios",
|
|
71
|
+
workspaceId: pairing.workspaceId
|
|
45
72
|
});
|
|
46
73
|
writeJson(response, 201, pairing);
|
|
47
74
|
return;
|
|
48
75
|
}
|
|
49
|
-
const actor = await requireMobileDevice(request, input.store)
|
|
50
|
-
|
|
76
|
+
const actor = await requireMobileDevice(request, input.store).catch((error) => {
|
|
77
|
+
logDiagnostics(diagnostics, "warn", "mobile_request.rejected", {
|
|
78
|
+
error: errorDiagnostics(error),
|
|
79
|
+
hasAuthorization: hasAuthorizationHeader(request),
|
|
80
|
+
method,
|
|
81
|
+
path,
|
|
82
|
+
status: statusCode(error)
|
|
83
|
+
});
|
|
84
|
+
throw error;
|
|
85
|
+
});
|
|
86
|
+
logDiagnostics(diagnostics, "info", "mobile_request.authenticated", {
|
|
87
|
+
deviceId: actor.id,
|
|
88
|
+
method,
|
|
89
|
+
path,
|
|
90
|
+
platform: actor.platform
|
|
91
|
+
});
|
|
92
|
+
if (method === "GET" && (path === "/me" || path === "/api/mobile/bootstrap")) {
|
|
51
93
|
writeJson(response, 200, {
|
|
52
94
|
workspace: { id: LOCAL_WORKSPACE_ID, name: `${identity.macName} Local` },
|
|
53
95
|
phone: {
|
|
@@ -63,19 +105,29 @@ export async function startLocalControlServer(input) {
|
|
|
63
105
|
});
|
|
64
106
|
return;
|
|
65
107
|
}
|
|
66
|
-
if (
|
|
67
|
-
|
|
108
|
+
if (method === "GET" && path === "/api/mobile/projects") {
|
|
109
|
+
const projects = await input.codex.listProjects();
|
|
110
|
+
logDiagnostics(diagnostics, "info", "projects.list.result", {
|
|
111
|
+
count: projects.length,
|
|
112
|
+
deviceId: actor.id
|
|
113
|
+
});
|
|
114
|
+
writeJson(response, 200, { projects });
|
|
68
115
|
return;
|
|
69
116
|
}
|
|
70
|
-
if (
|
|
117
|
+
if (method === "GET" && path === "/api/mobile/codex/models") {
|
|
71
118
|
writeJson(response, 200, { models: await input.codex.listModelOptions() });
|
|
72
119
|
return;
|
|
73
120
|
}
|
|
74
|
-
if (
|
|
75
|
-
|
|
121
|
+
if (method === "GET" && path === "/api/mobile/codex/completions") {
|
|
122
|
+
const completions = await input.codex.listCompletionStates();
|
|
123
|
+
logDiagnostics(diagnostics, "info", "completion.states.list.result", {
|
|
124
|
+
count: completions.length,
|
|
125
|
+
deviceId: actor.id
|
|
126
|
+
});
|
|
127
|
+
writeJson(response, 200, { completions });
|
|
76
128
|
return;
|
|
77
129
|
}
|
|
78
|
-
if (
|
|
130
|
+
if (method === "POST" && path === "/api/mobile/notifications/register") {
|
|
79
131
|
const body = await readJson(request);
|
|
80
132
|
const subscription = await input.store.registerPushSubscription(actor.id, {
|
|
81
133
|
platform: stringValue(body.platform) || "ios",
|
|
@@ -89,81 +141,188 @@ export async function startLocalControlServer(input) {
|
|
|
89
141
|
provider: subscription.provider
|
|
90
142
|
}
|
|
91
143
|
});
|
|
144
|
+
logDiagnostics(diagnostics, "info", "push.register.result", {
|
|
145
|
+
deviceId: actor.id,
|
|
146
|
+
platform: subscription.platform,
|
|
147
|
+
provider: subscription.provider
|
|
148
|
+
});
|
|
92
149
|
return;
|
|
93
150
|
}
|
|
94
|
-
if (
|
|
95
|
-
await readJson(request);
|
|
151
|
+
if (method === "POST" && path === "/api/mobile/notifications/diagnostics") {
|
|
152
|
+
const body = await readJson(request);
|
|
153
|
+
logDiagnostics(diagnostics, "warn", "push.registration.diagnostic", {
|
|
154
|
+
deviceId: actor.id,
|
|
155
|
+
message: stringValue(body.message),
|
|
156
|
+
stage: stringValue(body.stage)
|
|
157
|
+
});
|
|
96
158
|
writeJson(response, 200, { ok: true });
|
|
97
159
|
return;
|
|
98
160
|
}
|
|
99
161
|
const projectConversationsMatch = matchPath(path, "/api/mobile/projects/:projectId/conversations");
|
|
100
|
-
if (projectConversationsMatch &&
|
|
101
|
-
|
|
102
|
-
|
|
162
|
+
if (projectConversationsMatch && method === "GET") {
|
|
163
|
+
const conversations = await input.codex.listProjectConversations(projectConversationsMatch.projectId);
|
|
164
|
+
logDiagnostics(diagnostics, "info", "conversations.list.result", {
|
|
165
|
+
count: conversations.length,
|
|
166
|
+
deviceId: actor.id,
|
|
167
|
+
projectId: projectConversationsMatch.projectId
|
|
103
168
|
});
|
|
169
|
+
writeJson(response, 200, { conversations });
|
|
104
170
|
return;
|
|
105
171
|
}
|
|
106
|
-
if (projectConversationsMatch &&
|
|
172
|
+
if (projectConversationsMatch && method === "POST") {
|
|
107
173
|
const body = await readJson(request);
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
prompt
|
|
174
|
+
const attachments = attachmentReferences(body.attachments);
|
|
175
|
+
const prompt = requiredPrompt(body);
|
|
176
|
+
logDiagnostics(diagnostics, "info", "conversation.start.request", {
|
|
177
|
+
...promptDiagnostics(prompt),
|
|
178
|
+
...attachmentDiagnostics(attachments),
|
|
179
|
+
deviceId: actor.id,
|
|
180
|
+
model: stringValue(body.model) || undefined,
|
|
181
|
+
projectId: projectConversationsMatch.projectId
|
|
182
|
+
});
|
|
183
|
+
let started;
|
|
184
|
+
try {
|
|
185
|
+
started = await input.codex.startConversation(projectConversationsMatch.projectId, {
|
|
186
|
+
attachments,
|
|
187
|
+
modelSettings: modelSettings(body),
|
|
188
|
+
prompt
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
const status = statusCode(error);
|
|
193
|
+
logDiagnostics(diagnostics, "error", "conversation.start.failure", {
|
|
194
|
+
error: errorDiagnostics(error),
|
|
195
|
+
projectId: projectConversationsMatch.projectId,
|
|
196
|
+
status
|
|
197
|
+
});
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
logDiagnostics(diagnostics, "info", "conversation.start.result", {
|
|
201
|
+
conversationId: started.conversationId,
|
|
202
|
+
projectId: projectConversationsMatch.projectId,
|
|
203
|
+
status: started.status
|
|
112
204
|
});
|
|
113
205
|
writeJson(response, 201, started);
|
|
114
206
|
return;
|
|
115
207
|
}
|
|
116
208
|
const messageMatch = matchPath(path, "/api/mobile/conversations/:conversationId/messages");
|
|
117
|
-
if (messageMatch &&
|
|
209
|
+
if (messageMatch && method === "GET") {
|
|
118
210
|
const afterSequence = numberQuery(url.searchParams.get("afterSequence"));
|
|
119
211
|
const includeRuntime = url.searchParams.get("includeRuntime") === "true";
|
|
212
|
+
const messages = await input.codex.listMessages(messageMatch.conversationId, {
|
|
213
|
+
afterSequence,
|
|
214
|
+
includeRuntime
|
|
215
|
+
});
|
|
216
|
+
logDiagnostics(diagnostics, "info", "messages.list.result", {
|
|
217
|
+
...messageCounts(messages),
|
|
218
|
+
afterSequence,
|
|
219
|
+
conversationId: messageMatch.conversationId,
|
|
220
|
+
includeRuntime,
|
|
221
|
+
returned: messages.length
|
|
222
|
+
});
|
|
120
223
|
writeJson(response, 200, {
|
|
121
|
-
messages
|
|
122
|
-
afterSequence,
|
|
123
|
-
includeRuntime
|
|
124
|
-
})
|
|
224
|
+
messages
|
|
125
225
|
});
|
|
126
226
|
return;
|
|
127
227
|
}
|
|
128
|
-
if (messageMatch &&
|
|
228
|
+
if (messageMatch && method === "POST") {
|
|
129
229
|
const body = await readJson(request);
|
|
130
|
-
const
|
|
131
|
-
|
|
230
|
+
const prompt = stringValue(body.content) || requiredPrompt(body);
|
|
231
|
+
const delivery = deliveryMode(body.delivery);
|
|
232
|
+
logDiagnostics(diagnostics, "info", "conversation.continue.request", {
|
|
233
|
+
...promptDiagnostics(prompt),
|
|
234
|
+
conversationId: messageMatch.conversationId,
|
|
235
|
+
delivery,
|
|
236
|
+
deviceId: actor.id
|
|
237
|
+
});
|
|
238
|
+
let continued;
|
|
239
|
+
try {
|
|
240
|
+
continued = await input.codex.continueConversation(messageMatch.conversationId, {
|
|
241
|
+
delivery,
|
|
242
|
+
prompt
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
const status = statusCode(error);
|
|
247
|
+
logDiagnostics(diagnostics, "error", "conversation.continue.failure", {
|
|
248
|
+
conversationId: messageMatch.conversationId,
|
|
249
|
+
error: errorDiagnostics(error),
|
|
250
|
+
status
|
|
251
|
+
});
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
logDiagnostics(diagnostics, "info", "conversation.continue.result", {
|
|
255
|
+
conversationId: continued.conversationId,
|
|
256
|
+
status: continued.status
|
|
132
257
|
});
|
|
133
258
|
const messages = await input.codex.listMessages(continued.conversationId);
|
|
134
259
|
writeJson(response, 201, { message: messages.at(-1) ?? null });
|
|
135
260
|
return;
|
|
136
261
|
}
|
|
137
262
|
const continueMatch = matchPath(path, "/api/mobile/conversations/:conversationId/continue");
|
|
138
|
-
if (continueMatch &&
|
|
263
|
+
if (continueMatch && method === "POST") {
|
|
139
264
|
const body = await readJson(request);
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
265
|
+
const attachments = attachmentReferences(body.attachments);
|
|
266
|
+
const delivery = deliveryMode(body.delivery);
|
|
267
|
+
const prompt = requiredPrompt(body);
|
|
268
|
+
logDiagnostics(diagnostics, "info", "conversation.continue.request", {
|
|
269
|
+
...promptDiagnostics(prompt),
|
|
270
|
+
...attachmentDiagnostics(attachments),
|
|
271
|
+
conversationId: continueMatch.conversationId,
|
|
272
|
+
delivery,
|
|
273
|
+
deviceId: actor.id,
|
|
274
|
+
model: stringValue(body.model) || undefined
|
|
275
|
+
});
|
|
276
|
+
let continued;
|
|
277
|
+
try {
|
|
278
|
+
continued = await input.codex.continueConversation(continueMatch.conversationId, {
|
|
279
|
+
attachments,
|
|
280
|
+
delivery,
|
|
281
|
+
modelSettings: modelSettings(body),
|
|
282
|
+
prompt
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
const status = statusCode(error);
|
|
287
|
+
logDiagnostics(diagnostics, "error", "conversation.continue.failure", {
|
|
288
|
+
conversationId: continueMatch.conversationId,
|
|
289
|
+
error: errorDiagnostics(error),
|
|
290
|
+
status
|
|
291
|
+
});
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
294
|
+
logDiagnostics(diagnostics, "info", "conversation.continue.result", {
|
|
295
|
+
conversationId: continued.conversationId,
|
|
296
|
+
status: continued.status
|
|
144
297
|
});
|
|
145
298
|
writeJson(response, 200, continued);
|
|
146
299
|
return;
|
|
147
300
|
}
|
|
148
|
-
if (
|
|
301
|
+
if (method === "POST" && path === "/api/mobile/attachments") {
|
|
149
302
|
const attachment = await saveAttachment(input.attachmentDirectory ?? defaultLocalAttachmentDirectory(), await readJson(request));
|
|
303
|
+
logDiagnostics(diagnostics, "info", "attachment.saved", {
|
|
304
|
+
deviceId: actor.id,
|
|
305
|
+
kind: attachment.kind,
|
|
306
|
+
mimeType: attachment.mimeType,
|
|
307
|
+
size: attachment.size
|
|
308
|
+
});
|
|
150
309
|
writeJson(response, 201, { attachment });
|
|
151
310
|
return;
|
|
152
311
|
}
|
|
153
|
-
if (
|
|
312
|
+
if (method === "GET" && path === "/remote-control/status") {
|
|
154
313
|
writeJson(response, 200, {
|
|
155
314
|
sessions: Array.from(sessions.values()).filter((session) => session.clientMachineId === actor.id)
|
|
156
315
|
});
|
|
157
316
|
return;
|
|
158
317
|
}
|
|
159
|
-
if (
|
|
318
|
+
if (method === "GET" &&
|
|
160
319
|
(path === "/api/remote-control/sessions" || path === "/remote-control/sessions")) {
|
|
161
320
|
writeJson(response, 200, {
|
|
162
321
|
sessions: Array.from(sessions.values()).filter((session) => session.clientMachineId === actor.id)
|
|
163
322
|
});
|
|
164
323
|
return;
|
|
165
324
|
}
|
|
166
|
-
if (
|
|
325
|
+
if (method === "POST" &&
|
|
167
326
|
(path === "/api/remote-control/sessions" || path === "/remote-control/start")) {
|
|
168
327
|
const body = await readJson(request);
|
|
169
328
|
const hostMachineId = stringValue(body.hostMachineId) || identity.macId;
|
|
@@ -189,7 +348,7 @@ export async function startLocalControlServer(input) {
|
|
|
189
348
|
return;
|
|
190
349
|
}
|
|
191
350
|
const remoteSessionMatch = matchPath(path, "/api/remote-control/sessions/:sessionId");
|
|
192
|
-
if (remoteSessionMatch &&
|
|
351
|
+
if (remoteSessionMatch && method === "DELETE") {
|
|
193
352
|
const session = requireRemoteSession(sessions, remoteSessionMatch.sessionId, actor.id);
|
|
194
353
|
const ended = { ...session, status: "ended", updatedAt: new Date().toISOString() };
|
|
195
354
|
sessions.set(session.id, ended);
|
|
@@ -197,7 +356,7 @@ export async function startLocalControlServer(input) {
|
|
|
197
356
|
return;
|
|
198
357
|
}
|
|
199
358
|
const signalMatch = matchPath(path, "/api/remote-control/sessions/:sessionId/signals");
|
|
200
|
-
if (signalMatch &&
|
|
359
|
+
if (signalMatch && method === "GET") {
|
|
201
360
|
requireRemoteSession(sessions, signalMatch.sessionId, actor.id);
|
|
202
361
|
writeJson(response, 200, {
|
|
203
362
|
signals: signals.filter((signal) => signal.sessionId === signalMatch.sessionId &&
|
|
@@ -205,8 +364,8 @@ export async function startLocalControlServer(input) {
|
|
|
205
364
|
});
|
|
206
365
|
return;
|
|
207
366
|
}
|
|
208
|
-
if ((signalMatch &&
|
|
209
|
-
(path === "/remote-control/input" &&
|
|
367
|
+
if ((signalMatch && method === "POST") ||
|
|
368
|
+
(path === "/remote-control/input" && method === "POST")) {
|
|
210
369
|
const body = await readJson(request);
|
|
211
370
|
const sessionId = signalMatch?.sessionId ?? stringValue(body.sessionId);
|
|
212
371
|
const session = requireRemoteSession(sessions, sessionId, actor.id);
|
|
@@ -227,6 +386,14 @@ export async function startLocalControlServer(input) {
|
|
|
227
386
|
}
|
|
228
387
|
catch (error) {
|
|
229
388
|
const status = statusCode(error);
|
|
389
|
+
if (status >= 500) {
|
|
390
|
+
logDiagnostics(diagnostics, "error", "mobile_request.failure", {
|
|
391
|
+
error: errorDiagnostics(error),
|
|
392
|
+
method,
|
|
393
|
+
path,
|
|
394
|
+
status
|
|
395
|
+
});
|
|
396
|
+
}
|
|
230
397
|
writeJson(response, status, { error: errorMessage(error) });
|
|
231
398
|
}
|
|
232
399
|
});
|
|
@@ -238,6 +405,12 @@ export async function startLocalControlServer(input) {
|
|
|
238
405
|
if (address && typeof address === "object" && input.endpoint.endsWith(":0")) {
|
|
239
406
|
endpoint = input.endpoint.replace(/:0$/u, `:${address.port}`);
|
|
240
407
|
}
|
|
408
|
+
logDiagnostics(diagnostics, "info", "local_control.server_start", {
|
|
409
|
+
bindHost: input.bindHost,
|
|
410
|
+
endpoint,
|
|
411
|
+
port: address && typeof address === "object" ? address.port : input.port,
|
|
412
|
+
transport: input.transport
|
|
413
|
+
});
|
|
241
414
|
resolve();
|
|
242
415
|
});
|
|
243
416
|
});
|
|
@@ -264,6 +437,9 @@ async function requireMobileDevice(request, store) {
|
|
|
264
437
|
});
|
|
265
438
|
}
|
|
266
439
|
}
|
|
440
|
+
function hasAuthorizationHeader(request) {
|
|
441
|
+
return typeof request.headers.authorization === "string" && request.headers.authorization !== "";
|
|
442
|
+
}
|
|
267
443
|
function requireRemoteSession(sessions, sessionId, clientMachineId) {
|
|
268
444
|
const session = sessions.get(sessionId);
|
|
269
445
|
if (!session || session.clientMachineId !== clientMachineId) {
|
|
@@ -361,6 +537,9 @@ async function saveAttachment(directory, body) {
|
|
|
361
537
|
size: bytes.byteLength
|
|
362
538
|
};
|
|
363
539
|
}
|
|
540
|
+
function deliveryMode(input) {
|
|
541
|
+
return input === "queue" || input === "steer" ? input : undefined;
|
|
542
|
+
}
|
|
364
543
|
function safeFileName(value) {
|
|
365
544
|
return value.replace(/[/\\]/gu, "_").slice(0, 240) || "attachment.bin";
|
|
366
545
|
}
|
|
@@ -386,6 +565,28 @@ function numberQuery(value) {
|
|
|
386
565
|
const parsed = Number(value);
|
|
387
566
|
return Number.isFinite(parsed) ? parsed : undefined;
|
|
388
567
|
}
|
|
568
|
+
function messageCounts(messages) {
|
|
569
|
+
const counts = {
|
|
570
|
+
assistant: 0,
|
|
571
|
+
runtime: 0,
|
|
572
|
+
user: 0,
|
|
573
|
+
visible: 0
|
|
574
|
+
};
|
|
575
|
+
for (const message of messages) {
|
|
576
|
+
if (message.role === "assistant") {
|
|
577
|
+
counts.assistant += 1;
|
|
578
|
+
counts.visible += 1;
|
|
579
|
+
}
|
|
580
|
+
else if (message.role === "user") {
|
|
581
|
+
counts.user += 1;
|
|
582
|
+
counts.visible += 1;
|
|
583
|
+
}
|
|
584
|
+
else if (message.role === "runtime") {
|
|
585
|
+
counts.runtime += 1;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return counts;
|
|
589
|
+
}
|
|
389
590
|
function statusCode(error) {
|
|
390
591
|
if (error &&
|
|
391
592
|
typeof error === "object" &&
|