@chatroomcp/chatroom 0.2.0 → 0.2.2
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/README.md +1 -1
- package/dist/app/application.d.ts +0 -1
- package/dist/app/application.js +2 -0
- package/dist/app/event-bus.d.ts +0 -2
- package/dist/app/event-bus.js +0 -1
- package/dist/app/external-access-registry.d.ts +0 -1
- package/dist/app/external-access-registry.js +0 -1
- package/dist/app/lifecycle.d.ts +0 -1
- package/dist/auth/auth-service.js +0 -4
- package/dist/auth/ingress-policy.d.ts +0 -1
- package/dist/auth/ingress-policy.js +0 -1
- package/dist/cli/run-cli.js +0 -1
- package/dist/config/types.d.ts +0 -1
- package/dist/core/auth/repository.d.ts +0 -23
- package/dist/core/auth/repository.js +0 -6
- package/dist/core/errors/chatroom-error.d.ts +0 -15
- package/dist/core/errors/chatroom-error.js +0 -15
- package/dist/core/operations/bounded-value.d.ts +0 -1
- package/dist/core/operations/bounded-value.js +26 -10
- package/dist/core/operations/redactor.js +0 -1
- package/dist/core/operations/repository.d.ts +0 -1
- package/dist/core/operations/types.d.ts +0 -4
- package/dist/core/operations/types.js +0 -2
- package/dist/infrastructure/database/app-database.js +4 -97
- package/dist/infrastructure/database/migrations.d.ts +2 -0
- package/dist/infrastructure/database/migrations.js +44 -0
- package/dist/infrastructure/database/operation-repository.js +3 -9
- package/dist/infrastructure/database/schema.d.ts +2 -0
- package/dist/infrastructure/database/schema.js +85 -0
- package/dist/infrastructure/http/http-server.js +0 -1
- package/dist/mcp/server/create-mcp-server.js +3 -3
- package/dist/mcp/server/plugin-mcp-registrar.d.ts +0 -2
- package/dist/mcp/server/plugin-mcp-registrar.js +0 -4
- package/dist/mcp/server/tool-support.d.ts +0 -61
- package/dist/mcp/server/tool-support.js +0 -54
- package/dist/native/linux/chatroom-computer-helper +0 -0
- package/dist/native/macos/ChatRoomComputerHelper.app/Contents/Info.plist +1 -1
- package/dist/native/macos/ChatRoomComputerHelper.app/Contents/MacOS/chatroom-computer-helper +0 -0
- package/dist/native/windows/chatroom-computer-helper.exe +0 -0
- package/dist/operations/operation-log.d.ts +0 -3
- package/dist/operations/operation-log.js +0 -15
- package/dist/plugins/cloud/api-client.js +1 -0
- package/dist/plugins/cloud/controller.d.ts +2 -0
- package/dist/plugins/cloud/controller.js +28 -5
- package/dist/plugins/cloud/tunnel-client.js +0 -4
- package/dist/plugins/computer/computer-native-host.d.ts +2 -1
- package/dist/plugins/computer/computer-native-host.js +107 -17
- package/dist/plugins/computer/computer-schemas.d.ts +1 -0
- package/dist/plugins/computer/computer-schemas.js +1 -1
- package/dist/plugins/computer/types.d.ts +1 -1
- package/dist/plugins/git/git-service.d.ts +33 -0
- package/dist/plugins/git/git-service.js +414 -0
- package/dist/plugins/git/plugin.d.ts +4 -0
- package/dist/plugins/git/plugin.js +12 -0
- package/dist/plugins/git/types.d.ts +32 -0
- package/dist/plugins/process/backend.d.ts +0 -1
- package/dist/plugins/process/head-tail-buffer.d.ts +0 -1
- package/dist/plugins/process/infrastructure/pipe-process-backend.js +0 -1
- package/dist/plugins/process/infrastructure/pty-process-backend.js +1 -4
- package/dist/plugins/process/process-supervisor.js +10 -23
- package/dist/plugins/process/types.d.ts +0 -5
- package/dist/plugins/types.d.ts +0 -1
- package/dist/plugins/web/api-types.d.ts +3 -7
- package/dist/plugins/web/http/api-router.d.ts +0 -1
- package/dist/plugins/web/http/api-router.js +2 -4
- package/dist/plugins/web/http/git-api-router.d.ts +3 -0
- package/dist/plugins/web/http/git-api-router.js +98 -0
- package/dist/plugins/web/http/workspace-api-router.js +15 -37
- package/dist/plugins/web/plugin.js +4 -6
- package/dist/plugins/web/runtime.d.ts +5 -27
- package/dist/plugins/web/runtime.js +3 -42
- package/dist/plugins/workspace/mcp.js +17 -135
- package/dist/plugins/workspace/metadata.d.ts +4 -0
- package/dist/plugins/workspace/metadata.js +105 -0
- package/dist/plugins/workspace/plugin.d.ts +2 -8
- package/dist/plugins/workspace/plugin.js +8 -20
- package/dist/plugins/workspace/types.d.ts +18 -0
- package/dist/plugins/workspace/{infrastructure/workspace-fs.d.ts → workspace-fs.d.ts} +2 -15
- package/dist/plugins/workspace/workspace-fs.js +111 -0
- package/dist/plugins/workspace/workspace-service.d.ts +7 -42
- package/dist/plugins/workspace/workspace-service.js +61 -315
- package/dist/presentation/http/http-utils.d.ts +0 -1
- package/dist/presentation/http/oauth-router.d.ts +0 -1
- package/dist/presentation/http/oauth-router.js +0 -1
- package/dist/web/assets/{index-BXlxEk8f.css → index-DFKEvyzR.css} +1 -1
- package/dist/web/assets/index-vzPPspuL.js +26 -0
- package/dist/web/index.html +2 -2
- package/package.json +4 -3
- package/dist/plugins/workspace/domain/filesystem.d.ts +0 -25
- package/dist/plugins/workspace/domain/git.d.ts +0 -7
- package/dist/plugins/workspace/domain/repository.d.ts +0 -16
- package/dist/plugins/workspace/domain/repository.js +0 -1
- package/dist/plugins/workspace/domain/review.d.ts +0 -28
- package/dist/plugins/workspace/domain/review.js +0 -1
- package/dist/plugins/workspace/domain/workspace.d.ts +0 -23
- package/dist/plugins/workspace/domain/workspace.js +0 -1
- package/dist/plugins/workspace/git/git-command-runner.d.ts +0 -6
- package/dist/plugins/workspace/git/git-command-runner.js +0 -15
- package/dist/plugins/workspace/git/git-service.d.ts +0 -14
- package/dist/plugins/workspace/git/git-service.js +0 -107
- package/dist/plugins/workspace/git/git-snapshot-service.d.ts +0 -33
- package/dist/plugins/workspace/git/git-snapshot-service.js +0 -352
- package/dist/plugins/workspace/infrastructure/workspace-fs.js +0 -340
- package/dist/plugins/workspace/infrastructure/workspace-repository.d.ts +0 -12
- package/dist/plugins/workspace/infrastructure/workspace-repository.js +0 -51
- package/dist/plugins/workspace/workspace-helpers.d.ts +0 -9
- package/dist/plugins/workspace/workspace-helpers.js +0 -67
- package/dist/plugins/workspace/worktree-review-service.d.ts +0 -16
- package/dist/plugins/workspace/worktree-review-service.js +0 -51
- package/dist/web/assets/index-DUM6zVHS.js +0 -26
- /package/dist/plugins/{workspace/domain/filesystem.js → git/types.js} +0 -0
- /package/dist/plugins/workspace/{domain/git.js → types.js} +0 -0
|
@@ -7,9 +7,9 @@ export function createChatRoomMcpHandler(plugins) {
|
|
|
7
7
|
});
|
|
8
8
|
}
|
|
9
9
|
function createServer(plugins) {
|
|
10
|
-
const server = new McpServer({
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
const server = new McpServer({
|
|
11
|
+
name: CHATROOM_NAME,
|
|
12
|
+
version: CHATROOM_VERSION,
|
|
13
13
|
});
|
|
14
14
|
plugins.registerMcp(server);
|
|
15
15
|
return server;
|
|
@@ -4,7 +4,6 @@ type PluginToolInput<Schema extends StandardSchemaWithJSON> = StandardSchemaWith
|
|
|
4
4
|
type PluginToolAction<Input> = string | ((input: Input) => string);
|
|
5
5
|
export interface PluginToolExecution {
|
|
6
6
|
readonly operationId: string;
|
|
7
|
-
/** Keep the operation running after the MCP handler returns. The owner must finish it later. */
|
|
8
7
|
deferCompletion(): void;
|
|
9
8
|
}
|
|
10
9
|
export interface PluginToolConfig<InputSchema extends StandardSchemaWithJSON, OutputSchema extends StandardSchemaWithJSON> {
|
|
@@ -20,7 +19,6 @@ export interface PluginToolConfig<InputSchema extends StandardSchemaWithJSON, Ou
|
|
|
20
19
|
};
|
|
21
20
|
present?: (output: unknown) => CallToolResult;
|
|
22
21
|
}
|
|
23
|
-
/** Framework-owned MCP registration boundary that guarantees every plugin tool is audited. */
|
|
24
22
|
export declare class PluginMcpRegistrar {
|
|
25
23
|
private readonly server;
|
|
26
24
|
private readonly operations;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { asChatRoomError } from "../../core/errors/chatroom-error.js";
|
|
2
2
|
import { mcpTool } from "./tool-support.js";
|
|
3
|
-
/** Framework-owned MCP registration boundary that guarantees every plugin tool is audited. */
|
|
4
3
|
export class PluginMcpRegistrar {
|
|
5
4
|
server;
|
|
6
5
|
operations;
|
|
@@ -53,9 +52,6 @@ function operationReferences(input) {
|
|
|
53
52
|
return {};
|
|
54
53
|
const record = input;
|
|
55
54
|
return {
|
|
56
|
-
...(typeof record.workspaceId === "string"
|
|
57
|
-
? { workspaceId: record.workspaceId }
|
|
58
|
-
: {}),
|
|
59
55
|
...(typeof record.processId === "string"
|
|
60
56
|
? { processId: record.processId }
|
|
61
57
|
: {}),
|
|
@@ -6,18 +6,6 @@ export declare const closedRead: {
|
|
|
6
6
|
idempotentHint: true;
|
|
7
7
|
openWorldHint: false;
|
|
8
8
|
};
|
|
9
|
-
export declare const localWrite: {
|
|
10
|
-
readOnlyHint: false;
|
|
11
|
-
destructiveHint: true;
|
|
12
|
-
idempotentHint: true;
|
|
13
|
-
openWorldHint: false;
|
|
14
|
-
};
|
|
15
|
-
export declare const localMutation: {
|
|
16
|
-
readOnlyHint: false;
|
|
17
|
-
destructiveHint: false;
|
|
18
|
-
idempotentHint: false;
|
|
19
|
-
openWorldHint: false;
|
|
20
|
-
};
|
|
21
9
|
export declare const destructiveLocalMutation: {
|
|
22
10
|
readOnlyHint: false;
|
|
23
11
|
destructiveHint: true;
|
|
@@ -30,55 +18,6 @@ export declare const openWorldMutation: {
|
|
|
30
18
|
idempotentHint: false;
|
|
31
19
|
openWorldHint: true;
|
|
32
20
|
};
|
|
33
|
-
export declare const workspaceOutputSchema: z.ZodObject<{
|
|
34
|
-
id: z.ZodString;
|
|
35
|
-
root: z.ZodString;
|
|
36
|
-
sourceRoot: z.ZodString;
|
|
37
|
-
mode: z.ZodEnum<{
|
|
38
|
-
checkout: "checkout";
|
|
39
|
-
worktree: "worktree";
|
|
40
|
-
}>;
|
|
41
|
-
createdAt: z.ZodString;
|
|
42
|
-
lastUsedAt: z.ZodString;
|
|
43
|
-
instructions: z.ZodArray<z.ZodString>;
|
|
44
|
-
skills: z.ZodArray<z.ZodString>;
|
|
45
|
-
capabilities: z.ZodObject<{
|
|
46
|
-
filesystem: z.ZodLiteral<"read-write">;
|
|
47
|
-
git: z.ZodBoolean;
|
|
48
|
-
skills: z.ZodBoolean;
|
|
49
|
-
}, z.core.$strip>;
|
|
50
|
-
}, z.core.$strip>;
|
|
51
|
-
export declare const fileReadOutputSchema: z.ZodObject<{
|
|
52
|
-
content: z.ZodString;
|
|
53
|
-
bytes: z.ZodNumber;
|
|
54
|
-
truncated: z.ZodBoolean;
|
|
55
|
-
}, z.core.$strip>;
|
|
56
|
-
export declare const fileWriteOutputSchema: z.ZodObject<{
|
|
57
|
-
path: z.ZodString;
|
|
58
|
-
bytes: z.ZodNumber;
|
|
59
|
-
}, z.core.$strip>;
|
|
60
|
-
export declare const fileInfoSchema: z.ZodObject<{
|
|
61
|
-
path: z.ZodString;
|
|
62
|
-
type: z.ZodEnum<{
|
|
63
|
-
file: "file";
|
|
64
|
-
directory: "directory";
|
|
65
|
-
symlink: "symlink";
|
|
66
|
-
}>;
|
|
67
|
-
size: z.ZodNumber;
|
|
68
|
-
modifiedAt: z.ZodString;
|
|
69
|
-
}, z.core.$strip>;
|
|
70
|
-
export declare const searchMatchSchema: z.ZodObject<{
|
|
71
|
-
path: z.ZodString;
|
|
72
|
-
line: z.ZodNumber;
|
|
73
|
-
column: z.ZodNumber;
|
|
74
|
-
preview: z.ZodString;
|
|
75
|
-
}, z.core.$strip>;
|
|
76
|
-
export declare const changeSetSchema: z.ZodObject<{
|
|
77
|
-
files: z.ZodArray<z.ZodString>;
|
|
78
|
-
replacements: z.ZodNumber;
|
|
79
|
-
bytesBefore: z.ZodNumber;
|
|
80
|
-
bytesAfter: z.ZodNumber;
|
|
81
|
-
}, z.core.$strip>;
|
|
82
21
|
export declare const processSnapshotSchema: z.ZodObject<{
|
|
83
22
|
processId: z.ZodString;
|
|
84
23
|
command: z.ZodString;
|
|
@@ -6,18 +6,6 @@ export const closedRead = {
|
|
|
6
6
|
idempotentHint: true,
|
|
7
7
|
openWorldHint: false,
|
|
8
8
|
};
|
|
9
|
-
export const localWrite = {
|
|
10
|
-
readOnlyHint: false,
|
|
11
|
-
destructiveHint: true,
|
|
12
|
-
idempotentHint: true,
|
|
13
|
-
openWorldHint: false,
|
|
14
|
-
};
|
|
15
|
-
export const localMutation = {
|
|
16
|
-
readOnlyHint: false,
|
|
17
|
-
destructiveHint: false,
|
|
18
|
-
idempotentHint: false,
|
|
19
|
-
openWorldHint: false,
|
|
20
|
-
};
|
|
21
9
|
export const destructiveLocalMutation = {
|
|
22
10
|
readOnlyHint: false,
|
|
23
11
|
destructiveHint: true,
|
|
@@ -30,48 +18,6 @@ export const openWorldMutation = {
|
|
|
30
18
|
idempotentHint: false,
|
|
31
19
|
openWorldHint: true,
|
|
32
20
|
};
|
|
33
|
-
export const workspaceOutputSchema = z.object({
|
|
34
|
-
id: z.string(),
|
|
35
|
-
root: z.string(),
|
|
36
|
-
sourceRoot: z.string(),
|
|
37
|
-
mode: z.enum(["checkout", "worktree"]),
|
|
38
|
-
createdAt: z.string(),
|
|
39
|
-
lastUsedAt: z.string(),
|
|
40
|
-
instructions: z.array(z.string()),
|
|
41
|
-
skills: z.array(z.string()),
|
|
42
|
-
capabilities: z.object({
|
|
43
|
-
filesystem: z.literal("read-write"),
|
|
44
|
-
git: z.boolean(),
|
|
45
|
-
skills: z.boolean(),
|
|
46
|
-
}),
|
|
47
|
-
});
|
|
48
|
-
export const fileReadOutputSchema = z.object({
|
|
49
|
-
content: z.string(),
|
|
50
|
-
bytes: z.number().int().nonnegative(),
|
|
51
|
-
truncated: z.boolean(),
|
|
52
|
-
});
|
|
53
|
-
export const fileWriteOutputSchema = z.object({
|
|
54
|
-
path: z.string(),
|
|
55
|
-
bytes: z.number().int().nonnegative(),
|
|
56
|
-
});
|
|
57
|
-
export const fileInfoSchema = z.object({
|
|
58
|
-
path: z.string(),
|
|
59
|
-
type: z.enum(["file", "directory", "symlink"]),
|
|
60
|
-
size: z.number().int().nonnegative(),
|
|
61
|
-
modifiedAt: z.string(),
|
|
62
|
-
});
|
|
63
|
-
export const searchMatchSchema = z.object({
|
|
64
|
-
path: z.string(),
|
|
65
|
-
line: z.number().int().positive(),
|
|
66
|
-
column: z.number().int().positive(),
|
|
67
|
-
preview: z.string(),
|
|
68
|
-
});
|
|
69
|
-
export const changeSetSchema = z.object({
|
|
70
|
-
files: z.array(z.string()),
|
|
71
|
-
replacements: z.number().int().nonnegative(),
|
|
72
|
-
bytesBefore: z.number().int().nonnegative(),
|
|
73
|
-
bytesAfter: z.number().int().nonnegative(),
|
|
74
|
-
});
|
|
75
21
|
export const processSnapshotSchema = z.object({
|
|
76
22
|
processId: z.string(),
|
|
77
23
|
command: z.string(),
|
|
Binary file
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<key>CFBundlePackageType</key><string>APPL</string>
|
|
9
9
|
<key>CFBundleVersion</key><string>1</string>
|
|
10
10
|
<key>CFBundleShortVersionString</key><string>1.0</string>
|
|
11
|
-
<key>LSMinimumSystemVersion</key><string>
|
|
11
|
+
<key>LSMinimumSystemVersion</key><string>14.0</string>
|
|
12
12
|
<key>LSUIElement</key><true/>
|
|
13
13
|
<key>NSHumanReadableCopyright</key><string>ChatRoom</string>
|
|
14
14
|
</dict></plist>
|
package/dist/native/macos/ChatRoomComputerHelper.app/Contents/MacOS/chatroom-computer-helper
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { Operation, OperationStart, OperationStatus } from "../core/operations/types.js";
|
|
2
2
|
import type { OperationQuery, OperationRepository } from "../core/operations/repository.js";
|
|
3
3
|
import type { RuntimeEventBus } from "../app/event-bus.js";
|
|
4
|
-
/** Persists redacted, bounded operations produced by ChatRoom plugins. */
|
|
5
4
|
export declare class OperationLog {
|
|
6
5
|
private readonly repository;
|
|
7
6
|
private readonly eventBus;
|
|
@@ -10,12 +9,10 @@ export declare class OperationLog {
|
|
|
10
9
|
constructor(repository: OperationRepository, eventBus: RuntimeEventBus, maxPayloadBytes: number);
|
|
11
10
|
start(request: OperationStart): Operation;
|
|
12
11
|
finish(operationId: string, status: Exclude<OperationStatus, "running">, output?: unknown, error?: unknown): Operation;
|
|
13
|
-
updateRunning(operationId: string, output: unknown): Operation;
|
|
14
12
|
run<T>(request: OperationStart, action: () => Promise<T>): Promise<T>;
|
|
15
13
|
list(query?: OperationQuery): Operation[];
|
|
16
14
|
get(operationId: string): Operation | null;
|
|
17
15
|
associate(operationId: string, references: {
|
|
18
|
-
workspaceId?: string | null;
|
|
19
16
|
processId?: string | null;
|
|
20
17
|
}): Operation;
|
|
21
18
|
clearHistory(): {
|
|
@@ -2,7 +2,6 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { boundJson } from "../core/operations/bounded-value.js";
|
|
3
3
|
import { SecretRedactor } from "../core/operations/redactor.js";
|
|
4
4
|
import { asChatRoomError, ChatRoomError, } from "../core/errors/chatroom-error.js";
|
|
5
|
-
/** Persists redacted, bounded operations produced by ChatRoom plugins. */
|
|
6
5
|
export class OperationLog {
|
|
7
6
|
repository;
|
|
8
7
|
eventBus;
|
|
@@ -21,7 +20,6 @@ export class OperationLog {
|
|
|
21
20
|
source: request.source,
|
|
22
21
|
action: request.action,
|
|
23
22
|
status: "running",
|
|
24
|
-
workspaceId: request.workspaceId ?? null,
|
|
25
23
|
processId: request.processId ?? null,
|
|
26
24
|
input: input.value,
|
|
27
25
|
output: null,
|
|
@@ -52,17 +50,6 @@ export class OperationLog {
|
|
|
52
50
|
this.eventBus.emit({ type: "operation", operation });
|
|
53
51
|
return operation;
|
|
54
52
|
}
|
|
55
|
-
updateRunning(operationId, output) {
|
|
56
|
-
const operation = this.require(operationId);
|
|
57
|
-
if (operation.status !== "running")
|
|
58
|
-
return operation;
|
|
59
|
-
const bounded = boundJson(this.redactor.redact(output), this.maxPayloadBytes);
|
|
60
|
-
operation.output = bounded.value;
|
|
61
|
-
operation.outputTruncated = bounded.truncated;
|
|
62
|
-
this.repository.update(operation);
|
|
63
|
-
this.eventBus.emit({ type: "operation", operation });
|
|
64
|
-
return operation;
|
|
65
|
-
}
|
|
66
53
|
async run(request, action) {
|
|
67
54
|
const operation = this.start(request);
|
|
68
55
|
try {
|
|
@@ -88,8 +75,6 @@ export class OperationLog {
|
|
|
88
75
|
}
|
|
89
76
|
associate(operationId, references) {
|
|
90
77
|
const operation = this.require(operationId);
|
|
91
|
-
if (references.workspaceId !== undefined)
|
|
92
|
-
operation.workspaceId = references.workspaceId;
|
|
93
78
|
if (references.processId !== undefined)
|
|
94
79
|
operation.processId = references.processId;
|
|
95
80
|
this.repository.update(operation);
|
|
@@ -18,6 +18,7 @@ export declare class CloudController {
|
|
|
18
18
|
private lastError;
|
|
19
19
|
private managementTimer;
|
|
20
20
|
private renewalTimer;
|
|
21
|
+
private statusRetryTimer;
|
|
21
22
|
private stopped;
|
|
22
23
|
private constructor();
|
|
23
24
|
static create(config: ChatRoomConfig, externalAccess?: ExternalAccessSink): Promise<CloudController>;
|
|
@@ -38,6 +39,7 @@ export declare class CloudController {
|
|
|
38
39
|
private refreshLeaseIfNeeded;
|
|
39
40
|
private connectTunnel;
|
|
40
41
|
private publishExternalAccess;
|
|
42
|
+
private scheduleStatusRetry;
|
|
41
43
|
private scheduleManagementSync;
|
|
42
44
|
private scheduleRenewal;
|
|
43
45
|
private validManagementSession;
|
|
@@ -13,6 +13,7 @@ export class CloudController {
|
|
|
13
13
|
lastError = null;
|
|
14
14
|
managementTimer = null;
|
|
15
15
|
renewalTimer = null;
|
|
16
|
+
statusRetryTimer = null;
|
|
16
17
|
stopped = true;
|
|
17
18
|
constructor(config, cloudApi, externalAccess) {
|
|
18
19
|
this.config = config;
|
|
@@ -28,6 +29,9 @@ export class CloudController {
|
|
|
28
29
|
return controller;
|
|
29
30
|
}
|
|
30
31
|
status() {
|
|
32
|
+
const lease = this.state.lease && Date.parse(this.state.lease.expiresAt) > Date.now()
|
|
33
|
+
? this.state.lease
|
|
34
|
+
: null;
|
|
31
35
|
return {
|
|
32
36
|
installationId: this.state.installationId,
|
|
33
37
|
customerId: this.state.customerId,
|
|
@@ -37,10 +41,8 @@ export class CloudController {
|
|
|
37
41
|
managementSessionActive: Boolean(this.state.managementSession &&
|
|
38
42
|
Date.parse(this.state.managementSession.expiresAt) > Date.now()),
|
|
39
43
|
connection: this.connection,
|
|
40
|
-
mcpUrl:
|
|
41
|
-
|
|
42
|
-
: null,
|
|
43
|
-
webUrl: this.state.lease?.webBaseUrl ?? null,
|
|
44
|
+
mcpUrl: lease?.mcpBaseUrl ? `${lease.mcpBaseUrl}/mcp` : null,
|
|
45
|
+
webUrl: lease?.webBaseUrl ?? null,
|
|
44
46
|
lastError: this.lastError,
|
|
45
47
|
};
|
|
46
48
|
}
|
|
@@ -48,7 +50,10 @@ export class CloudController {
|
|
|
48
50
|
if (!this.stopped)
|
|
49
51
|
return;
|
|
50
52
|
this.stopped = false;
|
|
51
|
-
await this.syncStatus().catch((error) =>
|
|
53
|
+
await this.syncStatus().catch((error) => {
|
|
54
|
+
this.setError(error);
|
|
55
|
+
this.scheduleStatusRetry();
|
|
56
|
+
});
|
|
52
57
|
this.scheduleManagementSync();
|
|
53
58
|
}
|
|
54
59
|
async stop() {
|
|
@@ -57,8 +62,11 @@ export class CloudController {
|
|
|
57
62
|
clearTimeout(this.managementTimer);
|
|
58
63
|
if (this.renewalTimer)
|
|
59
64
|
clearTimeout(this.renewalTimer);
|
|
65
|
+
if (this.statusRetryTimer)
|
|
66
|
+
clearTimeout(this.statusRetryTimer);
|
|
60
67
|
this.managementTimer = null;
|
|
61
68
|
this.renewalTimer = null;
|
|
69
|
+
this.statusRetryTimer = null;
|
|
62
70
|
this.tunnel?.stop();
|
|
63
71
|
this.tunnel = null;
|
|
64
72
|
this.externalAccess?.clearCloud();
|
|
@@ -106,6 +114,9 @@ export class CloudController {
|
|
|
106
114
|
this.scheduleRenewal();
|
|
107
115
|
}
|
|
108
116
|
this.lastError = null;
|
|
117
|
+
if (this.statusRetryTimer)
|
|
118
|
+
clearTimeout(this.statusRetryTimer);
|
|
119
|
+
this.statusRetryTimer = null;
|
|
109
120
|
return this.status();
|
|
110
121
|
}
|
|
111
122
|
async restore(recoveryKey) {
|
|
@@ -222,6 +233,18 @@ export class CloudController {
|
|
|
222
233
|
: null,
|
|
223
234
|
});
|
|
224
235
|
}
|
|
236
|
+
scheduleStatusRetry(delayMs = 30_000) {
|
|
237
|
+
if (this.stopped || this.statusRetryTimer)
|
|
238
|
+
return;
|
|
239
|
+
this.statusRetryTimer = setTimeout(() => {
|
|
240
|
+
this.statusRetryTimer = null;
|
|
241
|
+
void this.syncStatus().catch((error) => {
|
|
242
|
+
this.setError(error);
|
|
243
|
+
this.scheduleStatusRetry();
|
|
244
|
+
});
|
|
245
|
+
}, delayMs);
|
|
246
|
+
this.statusRetryTimer.unref();
|
|
247
|
+
}
|
|
225
248
|
scheduleManagementSync(delayMs = 3_000) {
|
|
226
249
|
if (this.managementTimer)
|
|
227
250
|
clearTimeout(this.managementTimer);
|
|
@@ -77,15 +77,11 @@ export class CloudTunnelClient {
|
|
|
77
77
|
this.acceptingServices = new Set(lease.services);
|
|
78
78
|
if (this.stopped)
|
|
79
79
|
return;
|
|
80
|
-
// A new signed lease on the same tunnel is applied in-band. This keeps the
|
|
81
|
-
// current WebUI/API request alive while service permissions change.
|
|
82
80
|
if (!tunnelChanged) {
|
|
83
81
|
if (this.socket?.readyState === WebSocket.OPEN)
|
|
84
82
|
this.send({ type: "update-lease", lease: lease.token });
|
|
85
83
|
return;
|
|
86
84
|
}
|
|
87
|
-
// Changing the tunnel endpoint still requires a reconnect. If a service is
|
|
88
|
-
// being removed, let current streams finish before moving the connection.
|
|
89
85
|
if (!removesService || this.streams.size === 0) {
|
|
90
86
|
this.socket?.close();
|
|
91
87
|
return;
|
|
@@ -15,9 +15,10 @@ export declare class ComputerNativeHost {
|
|
|
15
15
|
restart(reason?: string): void;
|
|
16
16
|
dispose(): Promise<void>;
|
|
17
17
|
private ensureStarted;
|
|
18
|
+
private startHelper;
|
|
18
19
|
private startMacHelper;
|
|
19
20
|
private attachSocket;
|
|
20
|
-
private
|
|
21
|
+
private startPipeHelper;
|
|
21
22
|
private failTransport;
|
|
22
23
|
private reset;
|
|
23
24
|
private rejectPending;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
2
|
+
import { spawn, spawnSync, } from "node:child_process";
|
|
3
3
|
import { chmodSync, existsSync, rmSync } from "node:fs";
|
|
4
4
|
import { createServer } from "node:net";
|
|
5
5
|
import { createInterface } from "node:readline";
|
|
@@ -21,6 +21,8 @@ export class ComputerNativeHost {
|
|
|
21
21
|
return "macos";
|
|
22
22
|
if (process.platform === "win32")
|
|
23
23
|
return "windows";
|
|
24
|
+
if (process.platform === "linux")
|
|
25
|
+
return "linux";
|
|
24
26
|
return "unsupported";
|
|
25
27
|
}
|
|
26
28
|
get idle() {
|
|
@@ -59,18 +61,16 @@ export class ComputerNativeHost {
|
|
|
59
61
|
this.reset(new Error("Computer helper stopped"));
|
|
60
62
|
}
|
|
61
63
|
async ensureStarted() {
|
|
62
|
-
|
|
64
|
+
const platform = this.platform;
|
|
65
|
+
if (platform === "macos" && this.socket && !this.socket.destroyed)
|
|
63
66
|
return;
|
|
64
|
-
if (
|
|
67
|
+
if ((platform === "windows" || platform === "linux") &&
|
|
68
|
+
this.child &&
|
|
69
|
+
!this.child.killed)
|
|
65
70
|
return;
|
|
66
71
|
if (this.starting)
|
|
67
72
|
return this.starting;
|
|
68
|
-
this.starting =
|
|
69
|
-
this.platform === "macos"
|
|
70
|
-
? this.startMacHelper()
|
|
71
|
-
: this.platform === "windows"
|
|
72
|
-
? this.startWindowsHelper()
|
|
73
|
-
: Promise.reject(new ChatRoomError("UNSUPPORTED", "Computer Use is supported only on macOS and Windows"));
|
|
73
|
+
this.starting = this.startHelper(platform);
|
|
74
74
|
try {
|
|
75
75
|
await this.starting;
|
|
76
76
|
}
|
|
@@ -78,6 +78,18 @@ export class ComputerNativeHost {
|
|
|
78
78
|
this.starting = null;
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
+
startHelper(platform) {
|
|
82
|
+
switch (platform) {
|
|
83
|
+
case "macos":
|
|
84
|
+
return this.startMacHelper();
|
|
85
|
+
case "windows":
|
|
86
|
+
return this.startPipeHelper(windowsHelperPath(), "Windows");
|
|
87
|
+
case "linux":
|
|
88
|
+
return this.startPipeHelper(linuxHelperPath(), "Linux X11", linuxDesktopEnvironment());
|
|
89
|
+
case "unsupported":
|
|
90
|
+
return Promise.reject(new ChatRoomError("UNSUPPORTED", "Computer Use is supported only on macOS, Windows, and Linux X11"));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
81
93
|
async startMacHelper() {
|
|
82
94
|
const app = macHelperAppPath();
|
|
83
95
|
if (!app)
|
|
@@ -131,13 +143,13 @@ export class ComputerNativeHost {
|
|
|
131
143
|
socket.once("error", (error) => this.failTransport(socket, error));
|
|
132
144
|
socket.once("close", () => this.failTransport(socket, new Error("Computer helper exited")));
|
|
133
145
|
}
|
|
134
|
-
async
|
|
135
|
-
const executable = windowsHelperPath();
|
|
146
|
+
async startPipeHelper(executable, platformName, env = process.env) {
|
|
136
147
|
if (!executable)
|
|
137
|
-
throw new ChatRoomError("UNSUPPORTED",
|
|
148
|
+
throw new ChatRoomError("UNSUPPORTED", `${platformName} Computer helper is missing`);
|
|
138
149
|
const child = spawn(executable, [], {
|
|
139
150
|
stdio: ["pipe", "pipe", "pipe"],
|
|
140
|
-
windowsHide:
|
|
151
|
+
windowsHide: process.platform === "win32",
|
|
152
|
+
env,
|
|
141
153
|
});
|
|
142
154
|
this.child = child;
|
|
143
155
|
this.lines = createInterface({ input: child.stdout });
|
|
@@ -236,10 +248,88 @@ function projectRoot() {
|
|
|
236
248
|
return path.resolve(path.dirname(current), "../../..");
|
|
237
249
|
}
|
|
238
250
|
function macHelperAppPath() {
|
|
239
|
-
|
|
240
|
-
return existsSync(app) ? app : null;
|
|
251
|
+
return nativeHelperPath("macos", "ChatRoomComputerHelper.app");
|
|
241
252
|
}
|
|
242
253
|
function windowsHelperPath() {
|
|
243
|
-
|
|
244
|
-
|
|
254
|
+
return nativeHelperPath("windows", "chatroom-computer-helper.exe");
|
|
255
|
+
}
|
|
256
|
+
function linuxHelperPath() {
|
|
257
|
+
return nativeHelperPath("linux", "chatroom-computer-helper");
|
|
258
|
+
}
|
|
259
|
+
function nativeHelperPath(...parts) {
|
|
260
|
+
const target = path.join(projectRoot(), "dist", "native", ...parts);
|
|
261
|
+
return existsSync(target) ? target : null;
|
|
262
|
+
}
|
|
263
|
+
function linuxDesktopEnvironment() {
|
|
264
|
+
const env = { ...process.env };
|
|
265
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
266
|
+
if (!env.XDG_RUNTIME_DIR && uid !== null) {
|
|
267
|
+
const runtimeDir = `/run/user/${uid}`;
|
|
268
|
+
if (existsSync(runtimeDir))
|
|
269
|
+
env.XDG_RUNTIME_DIR = runtimeDir;
|
|
270
|
+
}
|
|
271
|
+
if (!env.DISPLAY) {
|
|
272
|
+
const display = activeX11Display(uid);
|
|
273
|
+
if (display) {
|
|
274
|
+
env.DISPLAY = display;
|
|
275
|
+
env.XDG_SESSION_TYPE = "x11";
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (!env.XAUTHORITY) {
|
|
279
|
+
const candidates = [
|
|
280
|
+
env.HOME ? path.join(env.HOME, ".Xauthority") : null,
|
|
281
|
+
env.XDG_RUNTIME_DIR
|
|
282
|
+
? path.join(env.XDG_RUNTIME_DIR, "gdm", "Xauthority")
|
|
283
|
+
: null,
|
|
284
|
+
];
|
|
285
|
+
const authority = candidates.find((candidate) => !!candidate && existsSync(candidate));
|
|
286
|
+
if (authority)
|
|
287
|
+
env.XAUTHORITY = authority;
|
|
288
|
+
}
|
|
289
|
+
if (!env.DBUS_SESSION_BUS_ADDRESS && env.XDG_RUNTIME_DIR) {
|
|
290
|
+
const bus = path.join(env.XDG_RUNTIME_DIR, "bus");
|
|
291
|
+
if (existsSync(bus))
|
|
292
|
+
env.DBUS_SESSION_BUS_ADDRESS = `unix:path=${bus}`;
|
|
293
|
+
}
|
|
294
|
+
return env;
|
|
295
|
+
}
|
|
296
|
+
function activeX11Display(uid) {
|
|
297
|
+
if (process.platform !== "linux" || uid === null)
|
|
298
|
+
return null;
|
|
299
|
+
const loginctl = existsSync("/usr/bin/loginctl")
|
|
300
|
+
? "/usr/bin/loginctl"
|
|
301
|
+
: "loginctl";
|
|
302
|
+
const sessions = spawnSync(loginctl, ["list-sessions", "--no-legend", "--no-pager"], {
|
|
303
|
+
encoding: "utf8",
|
|
304
|
+
timeout: 2_000,
|
|
305
|
+
});
|
|
306
|
+
if (sessions.status !== 0 || !sessions.stdout)
|
|
307
|
+
return null;
|
|
308
|
+
const userId = String(uid);
|
|
309
|
+
for (const line of sessions.stdout.split("\n")) {
|
|
310
|
+
const [sessionId, sessionUid] = line.trim().split(/\s+/, 3);
|
|
311
|
+
if (!sessionId || sessionUid !== userId)
|
|
312
|
+
continue;
|
|
313
|
+
const details = spawnSync(loginctl, [
|
|
314
|
+
"show-session",
|
|
315
|
+
sessionId,
|
|
316
|
+
"--no-pager",
|
|
317
|
+
"-p",
|
|
318
|
+
"Type",
|
|
319
|
+
"-p",
|
|
320
|
+
"Active",
|
|
321
|
+
"-p",
|
|
322
|
+
"Display",
|
|
323
|
+
], { encoding: "utf8", timeout: 2_000 });
|
|
324
|
+
if (details.status !== 0 || !details.stdout)
|
|
325
|
+
continue;
|
|
326
|
+
const values = Object.fromEntries(details.stdout
|
|
327
|
+
.split("\n")
|
|
328
|
+
.map((item) => item.split("=", 2))
|
|
329
|
+
.filter((item) => item.length === 2));
|
|
330
|
+
if (values.Type === "x11" && values.Active === "yes" && values.Display) {
|
|
331
|
+
return values.Display;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return null;
|
|
245
335
|
}
|
|
@@ -30,7 +30,7 @@ export const computerScreenshotSchema = z.object({
|
|
|
30
30
|
data: z.string(),
|
|
31
31
|
});
|
|
32
32
|
export const computerNativeStatusSchema = z.object({
|
|
33
|
-
platform: z.enum(["macos", "windows", "unsupported"]),
|
|
33
|
+
platform: z.enum(["macos", "windows", "linux", "unsupported"]),
|
|
34
34
|
helper: z.enum(["running", "stopped", "unavailable"]),
|
|
35
35
|
permissions: z.object({
|
|
36
36
|
accessibility: computerPermissionStateSchema,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type ComputerAccessScope = "local" | "remote";
|
|
2
|
-
export type ComputerPlatform = "macos" | "windows" | "unsupported";
|
|
2
|
+
export type ComputerPlatform = "macos" | "windows" | "linux" | "unsupported";
|
|
3
3
|
export type ComputerHelperState = "running" | "stopped" | "unavailable";
|
|
4
4
|
export type ComputerPermissionState = "granted" | "denied" | "unknown" | "not-required";
|
|
5
5
|
export type ComputerPermission = "accessibility" | "screenRecording";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { CommandRunner } from "../../core/runtime/command-runner.js";
|
|
2
|
+
import type { GitBranch, GitCommit, GitDiff, GitStatus } from "./types.js";
|
|
3
|
+
export declare class GitService {
|
|
4
|
+
private readonly commands;
|
|
5
|
+
constructor(commands: CommandRunner);
|
|
6
|
+
status(cwd: string): Promise<GitStatus | null>;
|
|
7
|
+
diff(cwd: string, filePath?: string, maxPreviewBytes?: number): Promise<GitDiff>;
|
|
8
|
+
stage(cwd: string, paths: string[]): Promise<GitStatus>;
|
|
9
|
+
unstage(cwd: string, paths: string[]): Promise<GitStatus>;
|
|
10
|
+
restore(cwd: string, filePath: string): Promise<GitStatus>;
|
|
11
|
+
commit(cwd: string, message: string): Promise<GitStatus>;
|
|
12
|
+
branches(cwd: string): Promise<GitBranch[]>;
|
|
13
|
+
createBranch(cwd: string, name: string): Promise<GitStatus>;
|
|
14
|
+
switchBranch(cwd: string, name: string): Promise<GitStatus>;
|
|
15
|
+
deleteBranch(cwd: string, name: string): Promise<GitStatus>;
|
|
16
|
+
log(cwd: string, limit?: number): Promise<GitCommit[]>;
|
|
17
|
+
fetch(cwd: string): Promise<GitStatus>;
|
|
18
|
+
pull(cwd: string): Promise<GitStatus>;
|
|
19
|
+
push(cwd: string): Promise<GitStatus>;
|
|
20
|
+
private run;
|
|
21
|
+
private repositoryRoot;
|
|
22
|
+
private requireRepository;
|
|
23
|
+
private requireStatus;
|
|
24
|
+
private head;
|
|
25
|
+
private requireHead;
|
|
26
|
+
private branch;
|
|
27
|
+
private upstream;
|
|
28
|
+
private aheadBehind;
|
|
29
|
+
private relatedPaths;
|
|
30
|
+
private pathExistsInHead;
|
|
31
|
+
private validateBranchName;
|
|
32
|
+
private withSnapshotIndex;
|
|
33
|
+
}
|