patchwork-os 0.2.0-alpha.2 → 0.2.0-alpha.21
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.bridge.md +6 -0
- package/README.md +13 -2
- package/dist/approvalHttp.d.ts +11 -2
- package/dist/approvalHttp.js +92 -9
- package/dist/approvalHttp.js.map +1 -1
- package/dist/approvalQueue.d.ts +12 -1
- package/dist/approvalQueue.js +25 -3
- package/dist/approvalQueue.js.map +1 -1
- package/dist/bridge.js +127 -23
- package/dist/bridge.js.map +1 -1
- package/dist/claudeDriver.d.ts +3 -1
- package/dist/claudeDriver.js +48 -0
- package/dist/claudeDriver.js.map +1 -1
- package/dist/claudeOrchestrator.d.ts +1 -1
- package/dist/claudeOrchestrator.js +14 -8
- package/dist/claudeOrchestrator.js.map +1 -1
- package/dist/commands/launchd.d.ts +2 -0
- package/dist/commands/launchd.js +94 -0
- package/dist/commands/launchd.js.map +1 -0
- package/dist/config.d.ts +7 -2
- package/dist/config.js +85 -8
- package/dist/config.js.map +1 -1
- package/dist/connectors/github.d.ts +58 -8
- package/dist/connectors/github.js +321 -84
- package/dist/connectors/github.js.map +1 -1
- package/dist/connectors/gmail.d.ts +4 -1
- package/dist/connectors/gmail.js +77 -16
- package/dist/connectors/gmail.js.map +1 -1
- package/dist/connectors/googleCalendar.d.ts +60 -0
- package/dist/connectors/googleCalendar.js +329 -0
- package/dist/connectors/googleCalendar.js.map +1 -0
- package/dist/connectors/linear.d.ts +117 -0
- package/dist/connectors/linear.js +248 -0
- package/dist/connectors/linear.js.map +1 -0
- package/dist/connectors/mcpClient.d.ts +56 -0
- package/dist/connectors/mcpClient.js +189 -0
- package/dist/connectors/mcpClient.js.map +1 -0
- package/dist/connectors/mcpOAuth.d.ts +83 -0
- package/dist/connectors/mcpOAuth.js +363 -0
- package/dist/connectors/mcpOAuth.js.map +1 -0
- package/dist/connectors/sentry.d.ts +43 -0
- package/dist/connectors/sentry.js +197 -0
- package/dist/connectors/sentry.js.map +1 -0
- package/dist/connectors/slack.d.ts +50 -0
- package/dist/connectors/slack.js +289 -0
- package/dist/connectors/slack.js.map +1 -0
- package/dist/drivers/claude/api.d.ts +11 -0
- package/dist/drivers/claude/api.js +54 -0
- package/dist/drivers/claude/api.js.map +1 -0
- package/dist/drivers/claude/envSanitizer.d.ts +7 -0
- package/dist/drivers/claude/envSanitizer.js +18 -0
- package/dist/drivers/claude/envSanitizer.js.map +1 -0
- package/dist/drivers/claude/streamParser.d.ts +38 -0
- package/dist/drivers/claude/streamParser.js +34 -0
- package/dist/drivers/claude/streamParser.js.map +1 -0
- package/dist/drivers/claude/subprocess.d.ts +19 -0
- package/dist/drivers/claude/subprocess.js +216 -0
- package/dist/drivers/claude/subprocess.js.map +1 -0
- package/dist/drivers/claude/subprocessSettings.d.ts +9 -0
- package/dist/drivers/claude/subprocessSettings.js +55 -0
- package/dist/drivers/claude/subprocessSettings.js.map +1 -0
- package/dist/drivers/gemini/index.d.ts +18 -0
- package/dist/drivers/gemini/index.js +210 -0
- package/dist/drivers/gemini/index.js.map +1 -0
- package/dist/drivers/grok/index.d.ts +11 -0
- package/dist/drivers/grok/index.js +22 -0
- package/dist/drivers/grok/index.js.map +1 -0
- package/dist/drivers/index.d.ts +23 -0
- package/dist/drivers/index.js +31 -0
- package/dist/drivers/index.js.map +1 -0
- package/dist/drivers/openai/index.d.ts +24 -0
- package/dist/drivers/openai/index.js +110 -0
- package/dist/drivers/openai/index.js.map +1 -0
- package/dist/drivers/types.d.ts +72 -0
- package/dist/drivers/types.js +30 -0
- package/dist/drivers/types.js.map +1 -0
- package/dist/index.js +35 -1
- package/dist/index.js.map +1 -1
- package/dist/installGuard.d.ts +25 -0
- package/dist/installGuard.js +48 -0
- package/dist/installGuard.js.map +1 -0
- package/dist/patchworkConfig.d.ts +9 -0
- package/dist/patchworkConfig.js.map +1 -1
- package/dist/recipes/scheduler.d.ts +23 -7
- package/dist/recipes/scheduler.js +135 -41
- package/dist/recipes/scheduler.js.map +1 -1
- package/dist/recipes/yamlRunner.d.ts +15 -0
- package/dist/recipes/yamlRunner.js +325 -26
- package/dist/recipes/yamlRunner.js.map +1 -1
- package/dist/recipesHttp.d.ts +14 -1
- package/dist/recipesHttp.js +21 -4
- package/dist/recipesHttp.js.map +1 -1
- package/dist/runLog.d.ts +5 -0
- package/dist/runLog.js +51 -1
- package/dist/runLog.js.map +1 -1
- package/dist/server.d.ts +15 -1
- package/dist/server.js +458 -31
- package/dist/server.js.map +1 -1
- package/dist/tools/addLinearComment.d.ts +55 -0
- package/dist/tools/addLinearComment.js +72 -0
- package/dist/tools/addLinearComment.js.map +1 -0
- package/dist/tools/bridgeDoctor.js +2 -2
- package/dist/tools/bridgeDoctor.js.map +1 -1
- package/dist/tools/createLinearIssue.d.ts +84 -0
- package/dist/tools/createLinearIssue.js +146 -0
- package/dist/tools/createLinearIssue.js.map +1 -0
- package/dist/tools/ctxGetTaskContext.d.ts +4 -1
- package/dist/tools/ctxGetTaskContext.js +45 -2
- package/dist/tools/ctxGetTaskContext.js.map +1 -1
- package/dist/tools/fetchCalendarEvents.d.ts +94 -0
- package/dist/tools/fetchCalendarEvents.js +97 -0
- package/dist/tools/fetchCalendarEvents.js.map +1 -0
- package/dist/tools/fetchGithubIssue.d.ts +80 -0
- package/dist/tools/fetchGithubIssue.js +84 -0
- package/dist/tools/fetchGithubIssue.js.map +1 -0
- package/dist/tools/fetchGithubPR.d.ts +89 -0
- package/dist/tools/fetchGithubPR.js +96 -0
- package/dist/tools/fetchGithubPR.js.map +1 -0
- package/dist/tools/fetchLinearIssue.d.ts +112 -0
- package/dist/tools/fetchLinearIssue.js +129 -0
- package/dist/tools/fetchLinearIssue.js.map +1 -0
- package/dist/tools/fetchSentryIssue.d.ts +143 -0
- package/dist/tools/fetchSentryIssue.js +150 -0
- package/dist/tools/fetchSentryIssue.js.map +1 -0
- package/dist/tools/fetchSlackProfile.d.ts +43 -0
- package/dist/tools/fetchSlackProfile.js +46 -0
- package/dist/tools/fetchSlackProfile.js.map +1 -0
- package/dist/tools/getConnectorStatus.d.ts +58 -0
- package/dist/tools/getConnectorStatus.js +56 -0
- package/dist/tools/getConnectorStatus.js.map +1 -0
- package/dist/tools/github/index.d.ts +1 -1
- package/dist/tools/github/index.js +1 -1
- package/dist/tools/github/index.js.map +1 -1
- package/dist/tools/github/pr.d.ts +122 -0
- package/dist/tools/github/pr.js +183 -0
- package/dist/tools/github/pr.js.map +1 -1
- package/dist/tools/index.js +27 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/slackListChannels.d.ts +65 -0
- package/dist/tools/slackListChannels.js +70 -0
- package/dist/tools/slackListChannels.js.map +1 -0
- package/dist/tools/slackPostMessage.d.ts +57 -0
- package/dist/tools/slackPostMessage.js +77 -0
- package/dist/tools/slackPostMessage.js.map +1 -0
- package/dist/tools/updateLinearIssue.d.ts +89 -0
- package/dist/tools/updateLinearIssue.js +117 -0
- package/dist/tools/updateLinearIssue.js.map +1 -0
- package/package.json +4 -2
- package/scripts/start-all.sh +56 -19
- package/templates/co.patchwork-os.bridge.plist +34 -0
- package/templates/recipes/ctx-loop-test.yaml +75 -0
- package/templates/recipes/morning-brief-slack.yaml +57 -0
- package/templates/recipes/morning-brief.yaml +21 -5
- package/templates/recipes/sentry-to-linear.yaml +77 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export declare function createSlackPostMessageTool(): {
|
|
2
|
+
schema: {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
annotations: {
|
|
6
|
+
readOnlyHint: boolean;
|
|
7
|
+
};
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: "object";
|
|
10
|
+
required: string[];
|
|
11
|
+
properties: {
|
|
12
|
+
channel: {
|
|
13
|
+
type: string;
|
|
14
|
+
description: string;
|
|
15
|
+
maxLength: number;
|
|
16
|
+
};
|
|
17
|
+
text: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
maxLength: number;
|
|
21
|
+
};
|
|
22
|
+
threadTs: {
|
|
23
|
+
type: string;
|
|
24
|
+
description: string;
|
|
25
|
+
maxLength: number;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
additionalProperties: false;
|
|
29
|
+
};
|
|
30
|
+
outputSchema: {
|
|
31
|
+
type: "object";
|
|
32
|
+
properties: {
|
|
33
|
+
ts: {
|
|
34
|
+
type: string;
|
|
35
|
+
};
|
|
36
|
+
channel: {
|
|
37
|
+
type: string;
|
|
38
|
+
};
|
|
39
|
+
slackConnected: {
|
|
40
|
+
type: string;
|
|
41
|
+
};
|
|
42
|
+
error: {
|
|
43
|
+
type: string;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
required: string[];
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
timeoutMs: number;
|
|
50
|
+
handler(args: Record<string, unknown>, signal?: AbortSignal): Promise<{
|
|
51
|
+
content: Array<{
|
|
52
|
+
type: string;
|
|
53
|
+
text: string;
|
|
54
|
+
}>;
|
|
55
|
+
structuredContent: unknown;
|
|
56
|
+
}>;
|
|
57
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { loadTokens, postMessage } from "../connectors/slack.js";
|
|
2
|
+
import { optionalString, requireString, successStructured } from "./utils.js";
|
|
3
|
+
export function createSlackPostMessageTool() {
|
|
4
|
+
return {
|
|
5
|
+
schema: {
|
|
6
|
+
name: "slackPostMessage",
|
|
7
|
+
description: "Post a message to a Slack channel. Use channel name (e.g. 'general') or channel ID. " +
|
|
8
|
+
"Optionally reply in a thread by providing threadTs.",
|
|
9
|
+
annotations: { readOnlyHint: false },
|
|
10
|
+
inputSchema: {
|
|
11
|
+
type: "object",
|
|
12
|
+
required: ["channel", "text"],
|
|
13
|
+
properties: {
|
|
14
|
+
channel: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "Channel name (e.g. 'general') or channel ID (e.g. 'C12345').",
|
|
17
|
+
maxLength: 200,
|
|
18
|
+
},
|
|
19
|
+
text: {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "Message text (Markdown supported via mrkdwn).",
|
|
22
|
+
maxLength: 40000,
|
|
23
|
+
},
|
|
24
|
+
threadTs: {
|
|
25
|
+
type: "string",
|
|
26
|
+
description: "Thread timestamp to reply in. Omit to post as a new message.",
|
|
27
|
+
maxLength: 50,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
additionalProperties: false,
|
|
31
|
+
},
|
|
32
|
+
outputSchema: {
|
|
33
|
+
type: "object",
|
|
34
|
+
properties: {
|
|
35
|
+
ts: { type: "string" },
|
|
36
|
+
channel: { type: "string" },
|
|
37
|
+
slackConnected: { type: "boolean" },
|
|
38
|
+
error: { type: "string" },
|
|
39
|
+
},
|
|
40
|
+
required: ["slackConnected"],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
timeoutMs: 15_000,
|
|
44
|
+
async handler(args, signal) {
|
|
45
|
+
const tokens = loadTokens();
|
|
46
|
+
if (!tokens) {
|
|
47
|
+
return successStructured({
|
|
48
|
+
ts: "",
|
|
49
|
+
channel: "",
|
|
50
|
+
slackConnected: false,
|
|
51
|
+
error: "Slack not connected. GET /connections/slack/authorize first.",
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const channel = requireString(args, "channel", 200);
|
|
55
|
+
const text = requireString(args, "text", 40000);
|
|
56
|
+
const threadTs = optionalString(args, "threadTs", 50);
|
|
57
|
+
try {
|
|
58
|
+
const result = await postMessage(channel, text, threadTs ?? undefined, signal);
|
|
59
|
+
return successStructured({
|
|
60
|
+
ts: result.ts,
|
|
61
|
+
channel: result.channel,
|
|
62
|
+
slackConnected: true,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
const notConnected = err instanceof Error && err.message.includes("not connected");
|
|
67
|
+
return successStructured({
|
|
68
|
+
ts: "",
|
|
69
|
+
channel: "",
|
|
70
|
+
slackConnected: !notConnected,
|
|
71
|
+
error: err instanceof Error ? err.message : String(err),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=slackPostMessage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slackPostMessage.js","sourceRoot":"","sources":["../../src/tools/slackPostMessage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export declare function createUpdateLinearIssueTool(): {
|
|
2
|
+
schema: {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
annotations: {
|
|
6
|
+
readOnlyHint: boolean;
|
|
7
|
+
};
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: "object";
|
|
10
|
+
required: string[];
|
|
11
|
+
properties: {
|
|
12
|
+
id: {
|
|
13
|
+
type: string;
|
|
14
|
+
description: string;
|
|
15
|
+
maxLength: number;
|
|
16
|
+
};
|
|
17
|
+
title: {
|
|
18
|
+
type: string;
|
|
19
|
+
maxLength: number;
|
|
20
|
+
};
|
|
21
|
+
description: {
|
|
22
|
+
type: string;
|
|
23
|
+
maxLength: number;
|
|
24
|
+
};
|
|
25
|
+
priority: {
|
|
26
|
+
type: string;
|
|
27
|
+
description: string;
|
|
28
|
+
minimum: number;
|
|
29
|
+
maximum: number;
|
|
30
|
+
};
|
|
31
|
+
state: {
|
|
32
|
+
type: string;
|
|
33
|
+
description: string;
|
|
34
|
+
maxLength: number;
|
|
35
|
+
};
|
|
36
|
+
assignee: {
|
|
37
|
+
type: string;
|
|
38
|
+
description: string;
|
|
39
|
+
maxLength: number;
|
|
40
|
+
};
|
|
41
|
+
labelNames: {
|
|
42
|
+
type: string;
|
|
43
|
+
description: string;
|
|
44
|
+
items: {
|
|
45
|
+
type: string;
|
|
46
|
+
maxLength: number;
|
|
47
|
+
};
|
|
48
|
+
maxItems: number;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
additionalProperties: false;
|
|
52
|
+
};
|
|
53
|
+
outputSchema: {
|
|
54
|
+
type: "object";
|
|
55
|
+
properties: {
|
|
56
|
+
id: {
|
|
57
|
+
type: string;
|
|
58
|
+
};
|
|
59
|
+
identifier: {
|
|
60
|
+
type: string;
|
|
61
|
+
};
|
|
62
|
+
title: {
|
|
63
|
+
type: string;
|
|
64
|
+
};
|
|
65
|
+
url: {
|
|
66
|
+
type: string;
|
|
67
|
+
};
|
|
68
|
+
state: {
|
|
69
|
+
type: string;
|
|
70
|
+
};
|
|
71
|
+
linearConnected: {
|
|
72
|
+
type: string;
|
|
73
|
+
};
|
|
74
|
+
error: {
|
|
75
|
+
type: string;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
required: string[];
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
timeoutMs: number;
|
|
82
|
+
handler(args: Record<string, unknown>, signal?: AbortSignal): Promise<{
|
|
83
|
+
content: Array<{
|
|
84
|
+
type: string;
|
|
85
|
+
text: string;
|
|
86
|
+
}>;
|
|
87
|
+
structuredContent: unknown;
|
|
88
|
+
}>;
|
|
89
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { loadTokens, updateIssue } from "../connectors/linear.js";
|
|
2
|
+
import { requireString, successStructured } from "./utils.js";
|
|
3
|
+
export function createUpdateLinearIssueTool() {
|
|
4
|
+
return {
|
|
5
|
+
schema: {
|
|
6
|
+
name: "updateLinearIssue",
|
|
7
|
+
description: "Update an existing Linear issue. Pass only the fields you want to change. Requires Linear connector connected.",
|
|
8
|
+
annotations: { readOnlyHint: false },
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: "object",
|
|
11
|
+
required: ["id"],
|
|
12
|
+
properties: {
|
|
13
|
+
id: {
|
|
14
|
+
type: "string",
|
|
15
|
+
description: "Issue identifier (e.g. 'ENG-42') or URL.",
|
|
16
|
+
maxLength: 200,
|
|
17
|
+
},
|
|
18
|
+
title: { type: "string", maxLength: 500 },
|
|
19
|
+
description: { type: "string", maxLength: 10000 },
|
|
20
|
+
priority: {
|
|
21
|
+
type: "integer",
|
|
22
|
+
description: "0=no priority, 1=urgent, 2=high, 3=medium, 4=low.",
|
|
23
|
+
minimum: 0,
|
|
24
|
+
maximum: 4,
|
|
25
|
+
},
|
|
26
|
+
state: {
|
|
27
|
+
type: "string",
|
|
28
|
+
description: "Workflow state name (e.g. 'In Progress', 'Done').",
|
|
29
|
+
maxLength: 100,
|
|
30
|
+
},
|
|
31
|
+
assignee: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "Assignee name or email.",
|
|
34
|
+
maxLength: 200,
|
|
35
|
+
},
|
|
36
|
+
labelNames: {
|
|
37
|
+
type: "array",
|
|
38
|
+
description: "Label names to set (replaces existing labels).",
|
|
39
|
+
items: { type: "string", maxLength: 100 },
|
|
40
|
+
maxItems: 10,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
additionalProperties: false,
|
|
44
|
+
},
|
|
45
|
+
outputSchema: {
|
|
46
|
+
type: "object",
|
|
47
|
+
properties: {
|
|
48
|
+
id: { type: "string" },
|
|
49
|
+
identifier: { type: "string" },
|
|
50
|
+
title: { type: "string" },
|
|
51
|
+
url: { type: "string" },
|
|
52
|
+
state: { type: "string" },
|
|
53
|
+
linearConnected: { type: "boolean" },
|
|
54
|
+
error: { type: "string" },
|
|
55
|
+
},
|
|
56
|
+
required: ["id", "identifier", "title", "url", "linearConnected"],
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
timeoutMs: 20_000,
|
|
60
|
+
async handler(args, signal) {
|
|
61
|
+
const tokens = loadTokens();
|
|
62
|
+
if (!tokens) {
|
|
63
|
+
return successStructured({
|
|
64
|
+
id: "",
|
|
65
|
+
identifier: "",
|
|
66
|
+
title: "",
|
|
67
|
+
url: "",
|
|
68
|
+
linearConnected: false,
|
|
69
|
+
error: "Linear not connected. GET /connections/linear/authorize first.",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const id = requireString(args, "id", 200);
|
|
73
|
+
const labelNames = Array.isArray(args.labelNames)
|
|
74
|
+
? args.labelNames
|
|
75
|
+
.filter((l) => typeof l === "string")
|
|
76
|
+
.map(String)
|
|
77
|
+
: undefined;
|
|
78
|
+
try {
|
|
79
|
+
const issue = await updateIssue({
|
|
80
|
+
id,
|
|
81
|
+
...(typeof args.title === "string" && { title: args.title }),
|
|
82
|
+
...(typeof args.description === "string" && {
|
|
83
|
+
description: args.description,
|
|
84
|
+
}),
|
|
85
|
+
...(typeof args.priority === "number" && {
|
|
86
|
+
priority: args.priority,
|
|
87
|
+
}),
|
|
88
|
+
...(typeof args.state === "string" && { state: args.state }),
|
|
89
|
+
...(typeof args.assignee === "string" && {
|
|
90
|
+
assignee: args.assignee,
|
|
91
|
+
}),
|
|
92
|
+
...(labelNames && labelNames.length > 0 && { labels: labelNames }),
|
|
93
|
+
}, signal);
|
|
94
|
+
return successStructured({
|
|
95
|
+
id: issue.id,
|
|
96
|
+
identifier: issue.identifier,
|
|
97
|
+
title: issue.title,
|
|
98
|
+
url: issue.url,
|
|
99
|
+
state: issue.state?.name ?? "",
|
|
100
|
+
linearConnected: true,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
const notConnected = err instanceof Error && err.message.includes("not connected");
|
|
105
|
+
return successStructured({
|
|
106
|
+
id: "",
|
|
107
|
+
identifier: "",
|
|
108
|
+
title: "",
|
|
109
|
+
url: "",
|
|
110
|
+
linearConnected: !notConnected,
|
|
111
|
+
error: err instanceof Error ? err.message : String(err),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=updateLinearIssue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updateLinearIssue.js","sourceRoot":"","sources":["../../src/tools/updateLinearIssue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE9D,MAAM,UAAU,2BAA2B;IACzC,OAAO;QACL,MAAM,EAAE;YACN,IAAI,EAAE,mBAAmB;YACzB,WAAW,EACT,gHAAgH;YAClH,WAAW,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE;YACpC,WAAW,EAAE;gBACX,IAAI,EAAE,QAAiB;gBACvB,QAAQ,EAAE,CAAC,IAAI,CAAC;gBAChB,UAAU,EAAE;oBACV,EAAE,EAAE;wBACF,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,0CAA0C;wBACvD,SAAS,EAAE,GAAG;qBACf;oBACD,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,EAAE;oBACzC,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE;oBACjD,QAAQ,EAAE;wBACR,IAAI,EAAE,SAAS;wBACf,WAAW,EAAE,mDAAmD;wBAChE,OAAO,EAAE,CAAC;wBACV,OAAO,EAAE,CAAC;qBACX;oBACD,KAAK,EAAE;wBACL,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,mDAAmD;wBAChE,SAAS,EAAE,GAAG;qBACf;oBACD,QAAQ,EAAE;wBACR,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,yBAAyB;wBACtC,SAAS,EAAE,GAAG;qBACf;oBACD,UAAU,EAAE;wBACV,IAAI,EAAE,OAAO;wBACb,WAAW,EAAE,gDAAgD;wBAC7D,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,EAAE;wBACzC,QAAQ,EAAE,EAAE;qBACb;iBACF;gBACD,oBAAoB,EAAE,KAAc;aACrC;YACD,YAAY,EAAE;gBACZ,IAAI,EAAE,QAAiB;gBACvB,UAAU,EAAE;oBACV,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACtB,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBAC9B,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACzB,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACvB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACzB,eAAe,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;oBACpC,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBAC1B;gBACD,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,CAAC;aAClE;SACF;QACD,SAAS,EAAE,MAAM;QACjB,KAAK,CAAC,OAAO,CAAC,IAA6B,EAAE,MAAoB;YAC/D,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;YAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO,iBAAiB,CAAC;oBACvB,EAAE,EAAE,EAAE;oBACN,UAAU,EAAE,EAAE;oBACd,KAAK,EAAE,EAAE;oBACT,GAAG,EAAE,EAAE;oBACP,eAAe,EAAE,KAAK;oBACtB,KAAK,EACH,gEAAgE;iBACnE,CAAC,CAAC;YACL,CAAC;YAED,MAAM,EAAE,GAAG,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;YAC1C,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC;gBAC/C,CAAC,CAAE,IAAI,CAAC,UAAwB;qBAC3B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;qBACpC,GAAG,CAAC,MAAM,CAAC;gBAChB,CAAC,CAAC,SAAS,CAAC;YAEd,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,WAAW,CAC7B;oBACE,EAAE;oBACF,GAAG,CAAC,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;oBAC5D,GAAG,CAAC,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,IAAI;wBAC1C,WAAW,EAAE,IAAI,CAAC,WAAW;qBAC9B,CAAC;oBACF,GAAG,CAAC,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI;wBACvC,QAAQ,EAAE,IAAI,CAAC,QAAQ;qBACxB,CAAC;oBACF,GAAG,CAAC,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;oBAC5D,GAAG,CAAC,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI;wBACvC,QAAQ,EAAE,IAAI,CAAC,QAAQ;qBACxB,CAAC;oBACF,GAAG,CAAC,UAAU,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;iBACnE,EACD,MAAM,CACP,CAAC;gBACF,OAAO,iBAAiB,CAAC;oBACvB,EAAE,EAAE,KAAK,CAAC,EAAE;oBACZ,UAAU,EAAE,KAAK,CAAC,UAAU;oBAC5B,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,GAAG,EAAE,KAAK,CAAC,GAAG;oBACd,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,IAAI,EAAE;oBAC9B,eAAe,EAAE,IAAI;iBACtB,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,YAAY,GAChB,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;gBAChE,OAAO,iBAAiB,CAAC;oBACvB,EAAE,EAAE,EAAE;oBACN,UAAU,EAAE,EAAE;oBACd,KAAK,EAAE,EAAE;oBACT,GAAG,EAAE,EAAE;oBACP,eAAe,EAAE,CAAC,YAAY;oBAC9B,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;iBACxD,CAAC,CAAC;YACL,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "patchwork-os",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
4
|
-
"description": "Patchwork OS
|
|
3
|
+
"version": "0.2.0-alpha.21",
|
|
4
|
+
"description": "Patchwork OS — proactive, multi-model AI teammate built on the Claude IDE Bridge. One agent, any model, works while you're away.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -95,12 +95,14 @@
|
|
|
95
95
|
"@opentelemetry/semantic-conventions": "^1.40.0",
|
|
96
96
|
"ajv": "^8.18.0",
|
|
97
97
|
"minimatch": "^10.0.0",
|
|
98
|
+
"node-cron": "^3.0.3",
|
|
98
99
|
"ws": "^8.18.0",
|
|
99
100
|
"yaml": "^2.8.3"
|
|
100
101
|
},
|
|
101
102
|
"devDependencies": {
|
|
102
103
|
"@biomejs/biome": "^2.4.10",
|
|
103
104
|
"@types/node": "^22.0.0",
|
|
105
|
+
"@types/node-cron": "^3.0.11",
|
|
104
106
|
"@types/ws": "^8.5.0",
|
|
105
107
|
"@vitest/coverage-v8": "^3.0.0",
|
|
106
108
|
"@vscode/ripgrep": "^1.17.1",
|
package/scripts/start-all.sh
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Full orchestrator for bridge + claude + remote-control.
|
|
3
|
-
# Manages all
|
|
2
|
+
# Full orchestrator for bridge + claude + remote-control + dashboard.
|
|
3
|
+
# Manages all processes in tmux panes with health monitoring.
|
|
4
4
|
#
|
|
5
5
|
# Pane layout:
|
|
6
6
|
# 0 — health monitor (orchestrator)
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
# 2 — claude --ide (Claude Code CLI, connects to bridge for IDE tools)
|
|
9
9
|
# 3 — claude remote-control --spawn=session (exposes the workspace to claude.ai;
|
|
10
10
|
# spawned sessions auto-discover the bridge via ~/.claude/ide/*.lock)
|
|
11
|
-
# 4 —
|
|
11
|
+
# 4 — Patchwork dashboard (Next.js, http://localhost:3200)
|
|
12
|
+
# 5 — SSH reverse tunnel to VPS (optional, enabled with --vps <user@host:port>)
|
|
12
13
|
# Forwards VPS_PORT on the remote host back to the local bridge port.
|
|
13
14
|
# Allows claude.ai integrations to use a static VPS URL instead of a
|
|
14
15
|
# rotating remote-control session URL.
|
|
@@ -20,6 +21,8 @@
|
|
|
20
21
|
# Options:
|
|
21
22
|
# --workspace <path> Directory to open in Claude (default: current directory)
|
|
22
23
|
# --full Register all ~95 bridge tools (git, terminal, file ops, HTTP, GitHub).
|
|
24
|
+
# --no-dashboard Skip starting the Patchwork dashboard (pane 4).
|
|
25
|
+
# --dashboard-port <N> Dashboard port (default: 3200).
|
|
23
26
|
# Default is slim mode (27 IDE-exclusive tools). Add --full if your
|
|
24
27
|
# workflow requires git/terminal/file tools.
|
|
25
28
|
# --notify <topic> Push notifications via ntfy.sh when remote-control connects or
|
|
@@ -48,6 +51,8 @@ NTFY_TOPIC=""
|
|
|
48
51
|
IDE_NAME=""
|
|
49
52
|
VPS=""
|
|
50
53
|
FULL_MODE=""
|
|
54
|
+
NO_DASHBOARD=""
|
|
55
|
+
DASHBOARD_PORT="3200"
|
|
51
56
|
AUTOMATION_POLICY=""
|
|
52
57
|
CLAUDE_DRIVER="subprocess"
|
|
53
58
|
BRIDGE_READY_TIMEOUT="${BRIDGE_READY_TIMEOUT:-30}"
|
|
@@ -64,6 +69,8 @@ while [[ $# -gt 0 ]]; do
|
|
|
64
69
|
--ide) IDE_NAME="$2"; shift 2 ;;
|
|
65
70
|
--vps) VPS="$2"; shift 2 ;;
|
|
66
71
|
--full) FULL_MODE="--full"; shift ;;
|
|
72
|
+
--no-dashboard) NO_DASHBOARD=1; shift ;;
|
|
73
|
+
--dashboard-port) DASHBOARD_PORT="$2"; shift 2 ;;
|
|
67
74
|
--automation-policy) AUTOMATION_POLICY="$2"; shift 2 ;;
|
|
68
75
|
--claude-driver) CLAUDE_DRIVER="$2"; shift 2 ;;
|
|
69
76
|
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
|
@@ -90,23 +97,31 @@ fi
|
|
|
90
97
|
|
|
91
98
|
# --- Bridge conflict check ---
|
|
92
99
|
# Detect an already-running bridge instance and abort early with a clear message.
|
|
93
|
-
#
|
|
100
|
+
# Validates: isBridge:true + live PID + process is actually a bridge (not Windsurf reusing PID)
|
|
101
|
+
# by checking whether the lock file's port has an active listener.
|
|
94
102
|
while IFS= read -r lock; do
|
|
95
103
|
[[ -f "$lock" ]] || continue
|
|
96
|
-
|
|
104
|
+
lock_info=$(python3 -c "
|
|
97
105
|
import json, sys
|
|
98
106
|
try:
|
|
99
107
|
d = json.load(open(sys.argv[1]))
|
|
100
108
|
if d.get('isBridge') is True:
|
|
101
|
-
print(d.get('pid', ''))
|
|
109
|
+
print(d.get('pid', ''), d.get('port', ''))
|
|
102
110
|
except Exception:
|
|
103
111
|
pass
|
|
104
112
|
" "$lock" 2>/dev/null || true)
|
|
113
|
+
bridge_pid="${lock_info%% *}"
|
|
114
|
+
bridge_port="${lock_info##* }"
|
|
105
115
|
if [[ -n "$bridge_pid" ]] && kill -0 "$bridge_pid" 2>/dev/null; then
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
116
|
+
# Cross-check: verify a process is actually listening on the lock's port.
|
|
117
|
+
# Windsurf may reuse PIDs from stale locks — if nothing listens on that port,
|
|
118
|
+
# the bridge is not actually running.
|
|
119
|
+
if [[ -n "$bridge_port" ]] && lsof -i ":${bridge_port}" 2>/dev/null | grep -q LISTEN; then
|
|
120
|
+
echo "Error: bridge already running (PID $bridge_pid, port $bridge_port, lock: $(basename "$lock"))." >&2
|
|
121
|
+
echo " Stop it first: kill $bridge_pid" >&2
|
|
122
|
+
echo " Or kill the tmux session: tmux kill-session -t claude-all" >&2
|
|
123
|
+
exit 1
|
|
124
|
+
fi
|
|
110
125
|
fi
|
|
111
126
|
done < <(ls ~/.claude/ide/*.lock 2>/dev/null)
|
|
112
127
|
|
|
@@ -141,6 +156,7 @@ echo "=== Claude IDE Bridge Full Orchestrator ==="
|
|
|
141
156
|
echo " Ctrl+C in any pane — stops that process"
|
|
142
157
|
echo " Ctrl+B then D (press Ctrl+B, release, then press D) — detach (keeps running)"
|
|
143
158
|
echo " tmux kill-session -t $SESSION — stop everything"
|
|
159
|
+
[[ -z "$NO_DASHBOARD" ]] && echo " Dashboard: http://localhost:${DASHBOARD_PORT}"
|
|
144
160
|
[[ -n "$NTFY_TOPIC" ]] && echo " Push notifications: ntfy.sh/$NTFY_TOPIC"
|
|
145
161
|
if [[ -z "$FULL_MODE" ]]; then
|
|
146
162
|
echo ""
|
|
@@ -276,11 +292,12 @@ while true; do
|
|
|
276
292
|
sleep \$delay
|
|
277
293
|
done'"
|
|
278
294
|
|
|
279
|
-
# --- Create additional panes (pane 0 = orchestrator, 1 = bridge, 2 = claude, 3 = remote-control, 4 = ssh tunnel) ---
|
|
295
|
+
# --- Create additional panes (pane 0 = orchestrator, 1 = bridge, 2 = claude, 3 = remote-control, 4 = dashboard, 5 = ssh tunnel) ---
|
|
280
296
|
tmux split-window -v -t "$SESSION" # pane 1 for bridge
|
|
281
297
|
tmux split-window -v -t "$SESSION" # pane 2 for claude --ide
|
|
282
298
|
tmux split-window -v -t "$SESSION" # pane 3 for claude remote-control
|
|
283
|
-
[[ -
|
|
299
|
+
[[ -z "$NO_DASHBOARD" ]] && tmux split-window -v -t "$SESSION" # pane 4 for dashboard (optional)
|
|
300
|
+
[[ -n "$TUNNEL_CMD" ]] && tmux split-window -v -t "$SESSION" # pane 5 for ssh tunnel (optional)
|
|
284
301
|
tmux select-layout -t "$SESSION" even-vertical
|
|
285
302
|
|
|
286
303
|
# --- Helper: wait for a NEW lock file (ignores pre-existing ones) ---
|
|
@@ -359,10 +376,20 @@ sleep 3
|
|
|
359
376
|
# Pane 3: Remote control with auto-restart loop
|
|
360
377
|
tmux send-keys -t "${SESSION}:0.3" "$REMOTE_CMD" Enter
|
|
361
378
|
|
|
362
|
-
# Pane 4:
|
|
379
|
+
# Pane 4: Dashboard (only if not --no-dashboard and dashboard dir exists)
|
|
380
|
+
DASHBOARD_DIR="$BRIDGE_DIR/dashboard"
|
|
381
|
+
if [[ -z "$NO_DASHBOARD" ]] && [[ -d "$DASHBOARD_DIR" ]]; then
|
|
382
|
+
BRIDGE_PORT=$(basename "$LOCK_FILE" .lock)
|
|
383
|
+
DASHBOARD_CMD="cd $(printf '%q' "$DASHBOARD_DIR") && PATCHWORK_BRIDGE_PORT=${BRIDGE_PORT} npm run dev"
|
|
384
|
+
notify "Starting dashboard on http://localhost:${DASHBOARD_PORT}"
|
|
385
|
+
tmux send-keys -t "${SESSION}:0.4" "$DASHBOARD_CMD" Enter
|
|
386
|
+
fi
|
|
387
|
+
|
|
388
|
+
# Pane 5: SSH reverse tunnel (only if --vps was set)
|
|
363
389
|
if [[ -n "$TUNNEL_CMD" ]]; then
|
|
364
390
|
notify "Starting SSH tunnel to ${VPS}..."
|
|
365
|
-
|
|
391
|
+
TUNNEL_PANE=$([[ -z "$NO_DASHBOARD" ]] && echo 5 || echo 4)
|
|
392
|
+
tmux send-keys -t "${SESSION}:0.${TUNNEL_PANE}" "$TUNNEL_CMD" Enter
|
|
366
393
|
fi
|
|
367
394
|
|
|
368
395
|
# --- Health monitor (runs in pane 0 — the orchestrator pane) ---
|
|
@@ -377,10 +404,12 @@ cleanup() {
|
|
|
377
404
|
tmux send-keys -t "${SESSION}:0.1" C-c 2>/dev/null
|
|
378
405
|
tmux send-keys -t "${SESSION}:0.2" C-c 2>/dev/null
|
|
379
406
|
tmux send-keys -t "${SESSION}:0.3" C-c 2>/dev/null
|
|
380
|
-
[[ -
|
|
407
|
+
[[ -z "$NO_DASHBOARD" ]] && tmux send-keys -t "${SESSION}:0.4" C-c 2>/dev/null
|
|
408
|
+
[[ -n "$TUNNEL_CMD" ]] && tmux send-keys -t "${SESSION}:0.${TUNNEL_PANE:-4}" C-c 2>/dev/null
|
|
381
409
|
# Wait up to 5s for panes to exit gracefully, then force-kill
|
|
382
410
|
local panes=(1 2 3)
|
|
383
|
-
[[ -
|
|
411
|
+
[[ -z "$NO_DASHBOARD" ]] && panes=(1 2 3 4)
|
|
412
|
+
[[ -n "$TUNNEL_CMD" ]] && panes=("${panes[@]}" "${TUNNEL_PANE:-4}")
|
|
384
413
|
for _ in $(seq 1 5); do
|
|
385
414
|
sleep 1
|
|
386
415
|
all_idle=true
|
|
@@ -396,7 +425,8 @@ cleanup() {
|
|
|
396
425
|
done
|
|
397
426
|
sleep 1
|
|
398
427
|
local rpanes=(3 2 1)
|
|
399
|
-
[[ -
|
|
428
|
+
[[ -z "$NO_DASHBOARD" ]] && rpanes=(4 3 2 1)
|
|
429
|
+
[[ -n "$TUNNEL_CMD" ]] && rpanes=("${TUNNEL_PANE:-4}" "${rpanes[@]}")
|
|
400
430
|
for pane in "${rpanes[@]}"; do
|
|
401
431
|
tmux kill-pane -t "${SESSION}:0.${pane}" 2>/dev/null || true
|
|
402
432
|
done
|
|
@@ -413,7 +443,8 @@ while true; do
|
|
|
413
443
|
tmux send-keys -t "${SESSION}:0.1" C-c
|
|
414
444
|
tmux send-keys -t "${SESSION}:0.2" C-c
|
|
415
445
|
tmux send-keys -t "${SESSION}:0.3" C-c
|
|
416
|
-
[[ -
|
|
446
|
+
[[ -z "$NO_DASHBOARD" ]] && tmux send-keys -t "${SESSION}:0.4" C-c
|
|
447
|
+
[[ -n "$TUNNEL_CMD" ]] && tmux send-keys -t "${SESSION}:0.${TUNNEL_PANE:-4}" C-c
|
|
417
448
|
# Wait for bridge (pane 1) and claude (pane 2) to actually stop
|
|
418
449
|
for _ in $(seq 1 10); do
|
|
419
450
|
pane1_cmd=$(tmux display-message -t "${SESSION}:0.1" -p '#{pane_current_command}' 2>/dev/null || echo "")
|
|
@@ -476,9 +507,15 @@ while true; do
|
|
|
476
507
|
sleep 3
|
|
477
508
|
# Restart remote-control
|
|
478
509
|
tmux send-keys -t "${SESSION}:0.3" "$REMOTE_CMD" Enter
|
|
510
|
+
# Restart dashboard with updated bridge port
|
|
511
|
+
if [[ -z "$NO_DASHBOARD" ]] && [[ -d "$DASHBOARD_DIR" ]]; then
|
|
512
|
+
NEW_BRIDGE_PORT=$(basename "$LOCK_FILE" .lock)
|
|
513
|
+
DASHBOARD_CMD="cd $(printf '%q' "$DASHBOARD_DIR") && PATCHWORK_BRIDGE_PORT=${NEW_BRIDGE_PORT} npm run dev"
|
|
514
|
+
tmux send-keys -t "${SESSION}:0.4" "$DASHBOARD_CMD" Enter
|
|
515
|
+
fi
|
|
479
516
|
# Restart SSH tunnel (port may have changed — tunnel cmd reads lock file dynamically)
|
|
480
517
|
if [[ -n "$TUNNEL_CMD" ]]; then
|
|
481
|
-
tmux send-keys -t "${SESSION}:0
|
|
518
|
+
tmux send-keys -t "${SESSION}:0.${TUNNEL_PANE:-4}" "$TUNNEL_CMD" Enter
|
|
482
519
|
fi
|
|
483
520
|
notify "All processes restarted"
|
|
484
521
|
else
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
3
|
+
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
4
|
+
<plist version="1.0">
|
|
5
|
+
<dict>
|
|
6
|
+
<key>Label</key>
|
|
7
|
+
<string>co.patchwork-os.bridge</string>
|
|
8
|
+
<key>ProgramArguments</key>
|
|
9
|
+
<array>
|
|
10
|
+
<string>__BINARY_PATH__</string>
|
|
11
|
+
<string>--claude-driver</string>
|
|
12
|
+
<string>subprocess</string>
|
|
13
|
+
</array>
|
|
14
|
+
<key>RunAtLoad</key>
|
|
15
|
+
<true/>
|
|
16
|
+
<key>KeepAlive</key>
|
|
17
|
+
<true/>
|
|
18
|
+
<key>StandardOutPath</key>
|
|
19
|
+
<string>__HOME__/Library/Logs/patchwork-os/bridge.log</string>
|
|
20
|
+
<key>StandardErrorPath</key>
|
|
21
|
+
<string>__HOME__/Library/Logs/patchwork-os/bridge.err</string>
|
|
22
|
+
<key>EnvironmentVariables</key>
|
|
23
|
+
<dict>
|
|
24
|
+
<key>HOME</key>
|
|
25
|
+
<string>__HOME__</string>
|
|
26
|
+
<key>PATH</key>
|
|
27
|
+
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
|
28
|
+
</dict>
|
|
29
|
+
<key>WorkingDirectory</key>
|
|
30
|
+
<string>__HOME__</string>
|
|
31
|
+
<key>ThrottleInterval</key>
|
|
32
|
+
<integer>30</integer>
|
|
33
|
+
</dict>
|
|
34
|
+
</plist>
|