@shipers-dev/multi 0.38.0 → 0.39.1
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/index.js +304 -214
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -33220,7 +33220,11 @@ async function runAcp(opts) {
|
|
|
33220
33220
|
} catch (e) {
|
|
33221
33221
|
throw new Error(`writeTextFile failed: ${fmtErr(e)}`);
|
|
33222
33222
|
}
|
|
33223
|
-
}
|
|
33223
|
+
},
|
|
33224
|
+
async extMethod(_method, _params) {
|
|
33225
|
+
return {};
|
|
33226
|
+
},
|
|
33227
|
+
async extNotification(_method, _params) {}
|
|
33224
33228
|
};
|
|
33225
33229
|
const conn = new ClientSideConnection(() => client, stream2);
|
|
33226
33230
|
try {
|
|
@@ -33289,8 +33293,7 @@ async function runAcp(opts) {
|
|
|
33289
33293
|
const u = params.update;
|
|
33290
33294
|
const kind = u.sessionUpdate;
|
|
33291
33295
|
switch (kind) {
|
|
33292
|
-
case "agent_message_chunk":
|
|
33293
|
-
case "agent_thought_chunk": {
|
|
33296
|
+
case "agent_message_chunk": {
|
|
33294
33297
|
const text = extractText(u.content);
|
|
33295
33298
|
if (text) {
|
|
33296
33299
|
chunkCount++;
|
|
@@ -33298,6 +33301,9 @@ async function runAcp(opts) {
|
|
|
33298
33301
|
}
|
|
33299
33302
|
break;
|
|
33300
33303
|
}
|
|
33304
|
+
case "agent_thought_chunk": {
|
|
33305
|
+
break;
|
|
33306
|
+
}
|
|
33301
33307
|
case "tool_call":
|
|
33302
33308
|
case "tool_call_update": {
|
|
33303
33309
|
chunkCount++;
|
|
@@ -33540,13 +33546,15 @@ function handleSessionUpdate(params) {
|
|
|
33540
33546
|
const kind = u.sessionUpdate;
|
|
33541
33547
|
const out = [];
|
|
33542
33548
|
switch (kind) {
|
|
33543
|
-
case "agent_message_chunk":
|
|
33544
|
-
case "agent_thought_chunk": {
|
|
33549
|
+
case "agent_message_chunk": {
|
|
33545
33550
|
const text = extractText2(u.content);
|
|
33546
33551
|
if (text)
|
|
33547
33552
|
out.push({ event_type: "assistant_text", payload: { text } });
|
|
33548
33553
|
break;
|
|
33549
33554
|
}
|
|
33555
|
+
case "agent_thought_chunk": {
|
|
33556
|
+
break;
|
|
33557
|
+
}
|
|
33550
33558
|
case "tool_call":
|
|
33551
33559
|
case "tool_call_update": {
|
|
33552
33560
|
out.push({ event_type: "tool_call", payload: {
|
|
@@ -33758,6 +33766,25 @@ var init_agent_workspaces = __esm(() => {
|
|
|
33758
33766
|
});
|
|
33759
33767
|
|
|
33760
33768
|
// ../lib/plans.ts
|
|
33769
|
+
function parseUiBlocks(text) {
|
|
33770
|
+
const blocks = [];
|
|
33771
|
+
const errors3 = [];
|
|
33772
|
+
if (!text)
|
|
33773
|
+
return { blocks, errors: errors3 };
|
|
33774
|
+
let i = 0;
|
|
33775
|
+
let m;
|
|
33776
|
+
UI_FENCE_RE.lastIndex = 0;
|
|
33777
|
+
while ((m = UI_FENCE_RE.exec(text)) !== null) {
|
|
33778
|
+
i++;
|
|
33779
|
+
try {
|
|
33780
|
+
const parsed = UiBlockSchema.parse(JSON.parse(m[1]));
|
|
33781
|
+
blocks.push(parsed);
|
|
33782
|
+
} catch (e) {
|
|
33783
|
+
errors3.push({ index: i, type: "multi-ui", message: e?.message || "invalid ui block" });
|
|
33784
|
+
}
|
|
33785
|
+
}
|
|
33786
|
+
return { blocks, errors: errors3 };
|
|
33787
|
+
}
|
|
33761
33788
|
function parsePlanBlocks(text) {
|
|
33762
33789
|
const actions = [];
|
|
33763
33790
|
const errors3 = [];
|
|
@@ -33807,52 +33834,13 @@ function parsePlanBlocks(text) {
|
|
|
33807
33834
|
}
|
|
33808
33835
|
return { actions, errors: errors3 };
|
|
33809
33836
|
}
|
|
33810
|
-
|
|
33811
|
-
const blocks = [];
|
|
33812
|
-
const spans = [];
|
|
33813
|
-
const errors3 = [];
|
|
33814
|
-
if (!text)
|
|
33815
|
-
return { blocks, spans, errors: errors3 };
|
|
33816
|
-
let i = 0;
|
|
33817
|
-
let m;
|
|
33818
|
-
UI_FENCE_RE.lastIndex = 0;
|
|
33819
|
-
while ((m = UI_FENCE_RE.exec(text)) !== null) {
|
|
33820
|
-
i += 1;
|
|
33821
|
-
const start3 = m.index;
|
|
33822
|
-
const end3 = m.index + m[0].length;
|
|
33823
|
-
let parsed;
|
|
33824
|
-
try {
|
|
33825
|
-
parsed = JSON.parse(m[1]);
|
|
33826
|
-
} catch (e) {
|
|
33827
|
-
errors3.push({ index: i, type: "multi-ui", message: `block ${i}: invalid JSON (${e.message})` });
|
|
33828
|
-
continue;
|
|
33829
|
-
}
|
|
33830
|
-
const r = UiBlockSchema.safeParse(parsed);
|
|
33831
|
-
if (!r.success) {
|
|
33832
|
-
const issues = r.error.issues.map((iss) => `${iss.path.join(".") || "<root>"}: ${iss.message}`);
|
|
33833
|
-
errors3.push({ index: i, type: "multi-ui", message: `block ${i}: ${issues.join("; ")}` });
|
|
33834
|
-
continue;
|
|
33835
|
-
}
|
|
33836
|
-
blocks.push(r.data);
|
|
33837
|
-
spans.push({ block: r.data, start: start3, end: end3 });
|
|
33838
|
-
}
|
|
33839
|
-
return { blocks, spans, errors: errors3 };
|
|
33840
|
-
}
|
|
33841
|
-
var PLAN_SCHEMA_VERSION = 4, EvalPolicy, Priority, AssigneeType, IssueStatus, SessionRole, Capabilities, HandoffExpect, SkillFile, PlanActionSchema, PlanEnvelopeSchema, FENCE_RE, UiBlockSchema, UI_FENCE_RE;
|
|
33837
|
+
var PLAN_SCHEMA_VERSION = 2, Priority, AssigneeType, IssueStatus, SessionRole, SkillFile, PlanActionSchema, PlanEnvelopeSchema, UiBlockSchema, UI_FENCE_RE, FENCE_RE;
|
|
33842
33838
|
var init_plans = __esm(() => {
|
|
33843
33839
|
init_zod();
|
|
33844
|
-
EvalPolicy = exports_external.object({
|
|
33845
|
-
evaluator_agent_id: exports_external.string().min(1),
|
|
33846
|
-
threshold: exports_external.number().min(0).max(1),
|
|
33847
|
-
on_fail: exports_external.enum(["retry", "reroute", "escalate"]).optional(),
|
|
33848
|
-
max_retries: exports_external.number().int().min(0).max(10).optional()
|
|
33849
|
-
});
|
|
33850
33840
|
Priority = exports_external.enum(["low", "medium", "high"]);
|
|
33851
33841
|
AssigneeType = exports_external.enum(["human", "agent"]);
|
|
33852
33842
|
IssueStatus = exports_external.enum(["todo", "in_progress", "done", "failed", "stopped", "cancelled"]);
|
|
33853
33843
|
SessionRole = exports_external.enum(["implementer", "reviewer", "test-fixer"]);
|
|
33854
|
-
Capabilities = exports_external.record(exports_external.string(), exports_external.unknown());
|
|
33855
|
-
HandoffExpect = exports_external.enum(["summary", "json", "none"]);
|
|
33856
33844
|
SkillFile = exports_external.object({ path: exports_external.string().min(1), content: exports_external.string() });
|
|
33857
33845
|
PlanActionSchema = exports_external.discriminatedUnion("type", [
|
|
33858
33846
|
exports_external.object({
|
|
@@ -33864,9 +33852,7 @@ var init_plans = __esm(() => {
|
|
|
33864
33852
|
assignee_type: AssigneeType.optional(),
|
|
33865
33853
|
assignee_id: exports_external.string().optional(),
|
|
33866
33854
|
parent_id: exports_external.string().optional(),
|
|
33867
|
-
blocked_by: exports_external.array(exports_external.string().min(1)).optional()
|
|
33868
|
-
eval_policy: EvalPolicy.optional(),
|
|
33869
|
-
await_children: exports_external.boolean().optional()
|
|
33855
|
+
blocked_by: exports_external.array(exports_external.string().min(1)).optional()
|
|
33870
33856
|
}),
|
|
33871
33857
|
exports_external.object({
|
|
33872
33858
|
type: exports_external.literal("update"),
|
|
@@ -33877,8 +33863,7 @@ var init_plans = __esm(() => {
|
|
|
33877
33863
|
priority: Priority.optional(),
|
|
33878
33864
|
assignee_type: AssigneeType.optional(),
|
|
33879
33865
|
assignee_id: exports_external.string().optional(),
|
|
33880
|
-
blocked_by: exports_external.array(exports_external.string().min(1)).optional()
|
|
33881
|
-
eval_policy: EvalPolicy.nullable().optional()
|
|
33866
|
+
blocked_by: exports_external.array(exports_external.string().min(1)).optional()
|
|
33882
33867
|
}),
|
|
33883
33868
|
exports_external.object({
|
|
33884
33869
|
type: exports_external.literal("delegate"),
|
|
@@ -33886,12 +33871,9 @@ var init_plans = __esm(() => {
|
|
|
33886
33871
|
assignee_id: exports_external.string().min(1)
|
|
33887
33872
|
}),
|
|
33888
33873
|
exports_external.object({
|
|
33889
|
-
type: exports_external.literal("
|
|
33890
|
-
|
|
33891
|
-
|
|
33892
|
-
expect: HandoffExpect.optional(),
|
|
33893
|
-
return_to: exports_external.string().optional(),
|
|
33894
|
-
title: exports_external.string().min(1).max(500).optional()
|
|
33874
|
+
type: exports_external.literal("issue.comment"),
|
|
33875
|
+
id: exports_external.string().min(1),
|
|
33876
|
+
body: exports_external.string().min(1).max(8000)
|
|
33895
33877
|
}),
|
|
33896
33878
|
exports_external.object({
|
|
33897
33879
|
type: exports_external.literal("issue.delete"),
|
|
@@ -33923,16 +33905,14 @@ var init_plans = __esm(() => {
|
|
|
33923
33905
|
agent_type: exports_external.string().min(1),
|
|
33924
33906
|
prompt: exports_external.string().optional(),
|
|
33925
33907
|
skill_ids: exports_external.array(exports_external.string()).optional(),
|
|
33926
|
-
allowed_tools: exports_external.array(exports_external.string()).optional()
|
|
33927
|
-
capabilities: Capabilities.optional()
|
|
33908
|
+
allowed_tools: exports_external.array(exports_external.string()).optional()
|
|
33928
33909
|
}),
|
|
33929
33910
|
exports_external.object({
|
|
33930
33911
|
type: exports_external.literal("agent.update"),
|
|
33931
33912
|
id: exports_external.string().min(1),
|
|
33932
33913
|
name: exports_external.string().min(1).max(120).optional(),
|
|
33933
33914
|
prompt: exports_external.string().nullable().optional(),
|
|
33934
|
-
allowed_tools: exports_external.array(exports_external.string()).nullable().optional()
|
|
33935
|
-
capabilities: Capabilities.nullable().optional()
|
|
33915
|
+
allowed_tools: exports_external.array(exports_external.string()).nullable().optional()
|
|
33936
33916
|
}),
|
|
33937
33917
|
exports_external.object({
|
|
33938
33918
|
type: exports_external.literal("skill.create"),
|
|
@@ -33958,25 +33938,18 @@ var init_plans = __esm(() => {
|
|
|
33958
33938
|
agent_id: exports_external.string().min(1),
|
|
33959
33939
|
role: SessionRole,
|
|
33960
33940
|
device_id: exports_external.string().optional()
|
|
33961
|
-
}),
|
|
33962
|
-
exports_external.object({
|
|
33963
|
-
type: exports_external.literal("eval.submit"),
|
|
33964
|
-
id: exports_external.string().min(1).optional(),
|
|
33965
|
-
score: exports_external.number().min(0).max(1),
|
|
33966
|
-
feedback: exports_external.string().min(1).max(8000),
|
|
33967
|
-
scores: exports_external.record(exports_external.string(), exports_external.number().min(0).max(1)).optional()
|
|
33968
33941
|
})
|
|
33969
33942
|
]);
|
|
33970
33943
|
PlanEnvelopeSchema = exports_external.object({
|
|
33971
33944
|
actions: exports_external.array(exports_external.unknown())
|
|
33972
33945
|
});
|
|
33973
|
-
FENCE_RE = /```multi-plan\s*\n([\s\S]*?)\n```/g;
|
|
33974
33946
|
UiBlockSchema = exports_external.object({
|
|
33975
33947
|
template: exports_external.string().min(1).max(20000),
|
|
33976
33948
|
data: exports_external.unknown().optional(),
|
|
33977
33949
|
height: exports_external.number().int().min(40).max(1200).optional()
|
|
33978
33950
|
});
|
|
33979
33951
|
UI_FENCE_RE = /```multi-ui\s*\n([\s\S]*?)\n```/g;
|
|
33952
|
+
FENCE_RE = /```multi-plan\s*\n([\s\S]*?)\n```/g;
|
|
33980
33953
|
});
|
|
33981
33954
|
|
|
33982
33955
|
// ../lib/chat.ts
|
|
@@ -33991,6 +33964,7 @@ var init_chat = __esm(() => {
|
|
|
33991
33964
|
title: exports_external.string(),
|
|
33992
33965
|
primary_agent_id: exports_external.string().nullable(),
|
|
33993
33966
|
device_id: exports_external.string().nullable(),
|
|
33967
|
+
runtime: exports_external.string().nullable(),
|
|
33994
33968
|
created_at: exports_external.number(),
|
|
33995
33969
|
updated_at: exports_external.number()
|
|
33996
33970
|
});
|
|
@@ -34032,12 +34006,14 @@ var init_chat = __esm(() => {
|
|
|
34032
34006
|
CreateChatBodySchema = exports_external.object({
|
|
34033
34007
|
title: exports_external.string().min(1).max(200),
|
|
34034
34008
|
primary_agent_id: exports_external.string().nullable().optional(),
|
|
34035
|
-
device_id: exports_external.string().nullable().optional()
|
|
34009
|
+
device_id: exports_external.string().nullable().optional(),
|
|
34010
|
+
runtime: exports_external.string().nullable().optional()
|
|
34036
34011
|
});
|
|
34037
34012
|
UpdateChatBodySchema = exports_external.object({
|
|
34038
34013
|
title: exports_external.string().min(1).max(200).optional(),
|
|
34039
34014
|
primary_agent_id: exports_external.string().nullable().optional(),
|
|
34040
|
-
device_id: exports_external.string().nullable().optional()
|
|
34015
|
+
device_id: exports_external.string().nullable().optional(),
|
|
34016
|
+
runtime: exports_external.string().nullable().optional()
|
|
34041
34017
|
});
|
|
34042
34018
|
});
|
|
34043
34019
|
|
|
@@ -34052,7 +34028,7 @@ var init_lib = __esm(() => {
|
|
|
34052
34028
|
|
|
34053
34029
|
// src/_impl/git-enforce.ts
|
|
34054
34030
|
import { spawn as spawn2 } from "node:child_process";
|
|
34055
|
-
import { existsSync as
|
|
34031
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
34056
34032
|
import { join as join9 } from "node:path";
|
|
34057
34033
|
function run4(cwd, cmd, args2) {
|
|
34058
34034
|
return new Promise((resolve2) => {
|
|
@@ -34092,7 +34068,7 @@ function enforceCommitAndPush(baseWorkingDir, issueKey, mode = "enforce") {
|
|
|
34092
34068
|
};
|
|
34093
34069
|
if (mode === "off")
|
|
34094
34070
|
return result;
|
|
34095
|
-
if (!
|
|
34071
|
+
if (!existsSync9(wt))
|
|
34096
34072
|
return result;
|
|
34097
34073
|
if (!(yield* isGitRepo2(wt)))
|
|
34098
34074
|
return result;
|
|
@@ -34180,7 +34156,7 @@ var git = (cwd, args2, op) => exports_Effect.tryPromise({
|
|
|
34180
34156
|
try: () => run4(cwd, "git", args2),
|
|
34181
34157
|
catch: (cause3) => new GitError({ op, message: `git ${args2.join(" ")} threw`, cause: cause3 })
|
|
34182
34158
|
}), isGitRepo2 = (dir) => exports_Effect.gen(function* () {
|
|
34183
|
-
if (!
|
|
34159
|
+
if (!existsSync9(dir))
|
|
34184
34160
|
return false;
|
|
34185
34161
|
const r = yield* git(dir, ["rev-parse", "--is-inside-work-tree"], "rev-parse");
|
|
34186
34162
|
return r.code === 0 && r.stdout === "true";
|
|
@@ -34192,14 +34168,14 @@ var init_git_enforce = __esm(() => {
|
|
|
34192
34168
|
|
|
34193
34169
|
// src/_impl/outbox.ts
|
|
34194
34170
|
import { Database as Database2 } from "bun:sqlite";
|
|
34195
|
-
import { existsSync as
|
|
34171
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync7 } from "fs";
|
|
34196
34172
|
import { homedir } from "os";
|
|
34197
34173
|
import { join as join10 } from "path";
|
|
34198
34174
|
function ensureDb() {
|
|
34199
34175
|
if (db)
|
|
34200
34176
|
return db;
|
|
34201
|
-
if (!
|
|
34202
|
-
|
|
34177
|
+
if (!existsSync10(MULTI_DIR3))
|
|
34178
|
+
mkdirSync7(MULTI_DIR3, { recursive: true });
|
|
34203
34179
|
db = new Database2(TASKS_DB_PATH2);
|
|
34204
34180
|
db.exec(`
|
|
34205
34181
|
CREATE TABLE IF NOT EXISTS stream_outbox (
|
|
@@ -34337,12 +34313,12 @@ var init_outbox = __esm(() => {
|
|
|
34337
34313
|
});
|
|
34338
34314
|
|
|
34339
34315
|
// src/_impl/run-task.ts
|
|
34340
|
-
import { mkdirSync as
|
|
34341
|
-
import { join as join11, dirname as
|
|
34316
|
+
import { mkdirSync as mkdirSync8, existsSync as existsSync11, writeFileSync as writeFileSync6, readFileSync as readFileSync9, appendFileSync as appendFileSync4, unlinkSync as unlinkSync5, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
34317
|
+
import { join as join11, dirname as dirname9 } from "path";
|
|
34342
34318
|
function ensureDirs() {
|
|
34343
34319
|
for (const d of [MULTI_DIR4, join11(MULTI_DIR4, "logs"), SKILLS_DIR2]) {
|
|
34344
|
-
if (!
|
|
34345
|
-
|
|
34320
|
+
if (!existsSync11(d))
|
|
34321
|
+
mkdirSync8(d, { recursive: true });
|
|
34346
34322
|
}
|
|
34347
34323
|
}
|
|
34348
34324
|
function log3(msg) {
|
|
@@ -34389,7 +34365,7 @@ function resolveEnforcementMode(task) {
|
|
|
34389
34365
|
async function handleRunTask(apiUrl, deviceId, task, detected, ctx) {
|
|
34390
34366
|
const issueId = task.issue_id;
|
|
34391
34367
|
const isFollowup = !!task.followup;
|
|
34392
|
-
const baseWorkingDir = task.working_dir &&
|
|
34368
|
+
const baseWorkingDir = task.working_dir && existsSync11(task.working_dir) ? task.working_dir : undefined;
|
|
34393
34369
|
const tenantWsId = task.tenant_workspace_id ?? null;
|
|
34394
34370
|
const projectId = task.project_id ?? null;
|
|
34395
34371
|
const ISSUE_BASE = tenantWsId && projectId ? `${apiUrl}/api/workspaces/${tenantWsId}/projects/${projectId}/issues/${issueId}` : null;
|
|
@@ -35080,7 +35056,7 @@ async function executePlanActions(apiUrl, parentTask, actions, ctx, parseErrors
|
|
|
35080
35056
|
continue;
|
|
35081
35057
|
}
|
|
35082
35058
|
lines.push(`- [ok] updated ${res.data.key}`);
|
|
35083
|
-
if ((a.status === "done" || a.status === "cancelled") && parentTask.working_dir &&
|
|
35059
|
+
if ((a.status === "done" || a.status === "cancelled") && parentTask.working_dir && existsSync11(parentTask.working_dir)) {
|
|
35084
35060
|
const targetKey = res.data?.key;
|
|
35085
35061
|
if (targetKey) {
|
|
35086
35062
|
const targetIssueId = res.data?.id || a.id;
|
|
@@ -35315,26 +35291,26 @@ function statusIcon(status3) {
|
|
|
35315
35291
|
}
|
|
35316
35292
|
}
|
|
35317
35293
|
async function resolveAcpAdapter(agentType, detectedPath) {
|
|
35318
|
-
if (agentType === "pi" && detectedPath &&
|
|
35294
|
+
if (agentType === "pi" && detectedPath && existsSync11(detectedPath)) {
|
|
35319
35295
|
return [detectedPath, "--mode", "rpc"];
|
|
35320
35296
|
}
|
|
35321
35297
|
const override = process.env.MULTI_ACP_ADAPTER?.trim();
|
|
35322
35298
|
const adapterNames = override ? [override] : ["claude-code-acp", "claude-agent-acp"];
|
|
35323
35299
|
for (const name of adapterNames) {
|
|
35324
|
-
if (name.startsWith("/") &&
|
|
35300
|
+
if (name.startsWith("/") && existsSync11(name))
|
|
35325
35301
|
return [name];
|
|
35326
35302
|
try {
|
|
35327
35303
|
const here = new URL(import.meta.url).pathname;
|
|
35328
35304
|
let dir = here;
|
|
35329
35305
|
for (let i = 0;i < 8; i++) {
|
|
35330
|
-
dir =
|
|
35306
|
+
dir = dirname9(dir);
|
|
35331
35307
|
const bin = join11(dir, "node_modules", ".bin", name);
|
|
35332
|
-
if (
|
|
35308
|
+
if (existsSync11(bin))
|
|
35333
35309
|
return [bin];
|
|
35334
35310
|
}
|
|
35335
35311
|
} catch {}
|
|
35336
35312
|
const global2 = join11(HOME4, ".bun", "install", "global", "node_modules", ".bin", name);
|
|
35337
|
-
if (
|
|
35313
|
+
if (existsSync11(global2))
|
|
35338
35314
|
return [global2];
|
|
35339
35315
|
}
|
|
35340
35316
|
return null;
|
|
@@ -35362,7 +35338,7 @@ async function downloadCommentAttachments(apiUrl, wsId, projectId, issueId, comm
|
|
|
35362
35338
|
const items = list.data?.results || list.data || [];
|
|
35363
35339
|
if (!Array.isArray(items) || items.length === 0)
|
|
35364
35340
|
return [];
|
|
35365
|
-
|
|
35341
|
+
mkdirSync8(destDir, { recursive: true });
|
|
35366
35342
|
const token = authTokenHeader();
|
|
35367
35343
|
const out = [];
|
|
35368
35344
|
for (const it of items) {
|
|
@@ -35382,9 +35358,9 @@ async function downloadCommentAttachments(apiUrl, wsId, projectId, issueId, comm
|
|
|
35382
35358
|
}
|
|
35383
35359
|
function authTokenHeader() {
|
|
35384
35360
|
try {
|
|
35385
|
-
if (!
|
|
35361
|
+
if (!existsSync11(CONFIG_PATH2))
|
|
35386
35362
|
return null;
|
|
35387
|
-
const raw = JSON.parse(
|
|
35363
|
+
const raw = JSON.parse(readFileSync9(CONFIG_PATH2, "utf8"));
|
|
35388
35364
|
const token = raw.token ?? raw.authToken;
|
|
35389
35365
|
return token ? `Bearer ${token}` : null;
|
|
35390
35366
|
} catch {
|
|
@@ -35392,7 +35368,7 @@ function authTokenHeader() {
|
|
|
35392
35368
|
}
|
|
35393
35369
|
}
|
|
35394
35370
|
async function uploadOutputDir(apiUrl, wsId, projectId, issueId, commentId, dir) {
|
|
35395
|
-
if (!
|
|
35371
|
+
if (!existsSync11(dir))
|
|
35396
35372
|
return 0;
|
|
35397
35373
|
const files = [];
|
|
35398
35374
|
const walk = (d, depth = 0) => {
|
|
@@ -35417,7 +35393,7 @@ async function uploadOutputDir(apiUrl, wsId, projectId, issueId, commentId, dir)
|
|
|
35417
35393
|
let uploaded = 0;
|
|
35418
35394
|
for (const f of files) {
|
|
35419
35395
|
try {
|
|
35420
|
-
const data =
|
|
35396
|
+
const data = readFileSync9(f);
|
|
35421
35397
|
const form = new FormData;
|
|
35422
35398
|
const blob = new Blob([data]);
|
|
35423
35399
|
form.append("file", blob, f.split("/").pop() || "file");
|
|
@@ -35432,7 +35408,7 @@ async function uploadOutputDir(apiUrl, wsId, projectId, issueId, commentId, dir)
|
|
|
35432
35408
|
if (res.ok) {
|
|
35433
35409
|
uploaded++;
|
|
35434
35410
|
try {
|
|
35435
|
-
|
|
35411
|
+
unlinkSync5(f);
|
|
35436
35412
|
} catch {}
|
|
35437
35413
|
}
|
|
35438
35414
|
} catch (e) {
|
|
@@ -40185,8 +40161,8 @@ var init_chat_doc = __esm(() => {
|
|
|
40185
40161
|
});
|
|
40186
40162
|
|
|
40187
40163
|
// src/_impl/chat-peer.ts
|
|
40188
|
-
import { existsSync as
|
|
40189
|
-
import { dirname as
|
|
40164
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "fs";
|
|
40165
|
+
import { dirname as dirname10, join as join12 } from "path";
|
|
40190
40166
|
|
|
40191
40167
|
class ChatPeer {
|
|
40192
40168
|
chatId;
|
|
@@ -40208,16 +40184,16 @@ class ChatPeer {
|
|
|
40208
40184
|
}
|
|
40209
40185
|
loadFromDisk() {
|
|
40210
40186
|
try {
|
|
40211
|
-
if (
|
|
40212
|
-
const bytes =
|
|
40187
|
+
if (existsSync12(this.snapshotPath)) {
|
|
40188
|
+
const bytes = readFileSync10(this.snapshotPath);
|
|
40213
40189
|
return importSnapshot(new Uint8Array(bytes));
|
|
40214
40190
|
}
|
|
40215
40191
|
} catch {}
|
|
40216
40192
|
return new import_loro_crdt2.LoroDoc;
|
|
40217
40193
|
}
|
|
40218
40194
|
persist() {
|
|
40219
|
-
if (!
|
|
40220
|
-
|
|
40195
|
+
if (!existsSync12(dirname10(this.snapshotPath)))
|
|
40196
|
+
mkdirSync9(dirname10(this.snapshotPath), { recursive: true });
|
|
40221
40197
|
writeFileSync7(this.snapshotPath, exportSnapshot(this.doc));
|
|
40222
40198
|
this.dirtySinceWrite = 0;
|
|
40223
40199
|
}
|
|
@@ -40384,12 +40360,6 @@ async function handleChatTurn(opts) {
|
|
|
40384
40360
|
return;
|
|
40385
40361
|
}
|
|
40386
40362
|
const chosen = opts.preferredRuntime && detected.find((d) => d.type === opts.preferredRuntime) || detected.find((d) => d.type === "claude-code") || detected[0];
|
|
40387
|
-
const adapter = await resolveAcpAdapter(chosen.type, chosen.path);
|
|
40388
|
-
if (!adapter) {
|
|
40389
|
-
opts.onDone(`error: no ACP adapter for ${chosen.type}`);
|
|
40390
|
-
return;
|
|
40391
|
-
}
|
|
40392
|
-
const sessionId = chatSessions.get(opts.chatId) || null;
|
|
40393
40363
|
let prompt = opts.prompt;
|
|
40394
40364
|
if (opts.systemPreamble && !chatPreambleSent.has(opts.chatId)) {
|
|
40395
40365
|
prompt = `${opts.systemPreamble}
|
|
@@ -40399,6 +40369,52 @@ async function handleChatTurn(opts) {
|
|
|
40399
40369
|
${opts.prompt}`;
|
|
40400
40370
|
chatPreambleSent.add(opts.chatId);
|
|
40401
40371
|
}
|
|
40372
|
+
if (ACPX_RUNTIMES.has(chosen.type)) {
|
|
40373
|
+
try {
|
|
40374
|
+
const { stopReason } = await runAcpx({
|
|
40375
|
+
agentType: chosen.type,
|
|
40376
|
+
prompt,
|
|
40377
|
+
cwd: opts.cwd,
|
|
40378
|
+
sessionName: `chat-${opts.chatId}`,
|
|
40379
|
+
onEvent: (ev) => {
|
|
40380
|
+
if (ev.event_type === "assistant_text") {
|
|
40381
|
+
const text = ev.payload?.text;
|
|
40382
|
+
if (typeof text === "string" && text.length)
|
|
40383
|
+
opts.onChunk(text);
|
|
40384
|
+
} else if (ev.event_type === "tool_call") {
|
|
40385
|
+
const p = ev.payload;
|
|
40386
|
+
opts.onToolCall?.({
|
|
40387
|
+
id: String(p?.id ?? ""),
|
|
40388
|
+
tool: String(p?.tool ?? "tool"),
|
|
40389
|
+
kind: p?.kind ? String(p.kind) : undefined,
|
|
40390
|
+
status: p?.status ? String(p.status) : undefined,
|
|
40391
|
+
input: p?.input
|
|
40392
|
+
});
|
|
40393
|
+
} else if (ev.event_type === "tool_result") {
|
|
40394
|
+
const p = ev.payload;
|
|
40395
|
+
opts.onToolResult?.({
|
|
40396
|
+
tool_call_id: String(p?.tool_use_id ?? p?.id ?? ""),
|
|
40397
|
+
content: typeof p?.content === "string" ? p.content : JSON.stringify(p?.content ?? "")
|
|
40398
|
+
});
|
|
40399
|
+
} else if (ev.event_type === "error") {
|
|
40400
|
+
const m = String(ev.payload?.message ?? "agent error");
|
|
40401
|
+
opts.onDone(`error: ${m}`);
|
|
40402
|
+
}
|
|
40403
|
+
}
|
|
40404
|
+
});
|
|
40405
|
+
opts.onDone(stopReason || "ok");
|
|
40406
|
+
} catch (e) {
|
|
40407
|
+
opts.log(`[chat-turn] ${opts.chatId} acpx crash: ${e.message}`);
|
|
40408
|
+
opts.onDone(`error: ${e.message}`);
|
|
40409
|
+
}
|
|
40410
|
+
return;
|
|
40411
|
+
}
|
|
40412
|
+
const adapter = await resolveAcpAdapter(chosen.type, chosen.path);
|
|
40413
|
+
if (!adapter) {
|
|
40414
|
+
opts.onDone(`error: no ACP adapter for ${chosen.type}`);
|
|
40415
|
+
return;
|
|
40416
|
+
}
|
|
40417
|
+
const sessionId = chatSessions.get(opts.chatId) || null;
|
|
40402
40418
|
try {
|
|
40403
40419
|
await runAcp({
|
|
40404
40420
|
apiUrl: "",
|
|
@@ -40445,11 +40461,13 @@ ${opts.prompt}`;
|
|
|
40445
40461
|
opts.onDone(`error: ${e.message}`);
|
|
40446
40462
|
}
|
|
40447
40463
|
}
|
|
40448
|
-
var chatSessions, chatPreambleSent;
|
|
40464
|
+
var ACPX_RUNTIMES, chatSessions, chatPreambleSent;
|
|
40449
40465
|
var init_chat_turn = __esm(() => {
|
|
40450
40466
|
init_acp_runner();
|
|
40467
|
+
init_acpx_runner();
|
|
40451
40468
|
init_run_task();
|
|
40452
40469
|
init_detect();
|
|
40470
|
+
ACPX_RUNTIMES = new Set(["pi", "codex", "openclaw"]);
|
|
40453
40471
|
chatSessions = new Map;
|
|
40454
40472
|
chatPreambleSent = new Set;
|
|
40455
40473
|
});
|
|
@@ -40559,6 +40577,16 @@ async function executeChatPlanActions(actionsIn, parseErrors, ctx) {
|
|
|
40559
40577
|
}
|
|
40560
40578
|
lines.push(`- [ok] handoff -> ${a.target_agent_id} via **${res.data?.child_key}** expect=${a.expect ?? "summary"}`);
|
|
40561
40579
|
tally(true);
|
|
40580
|
+
} else if (a.type === "issue.comment") {
|
|
40581
|
+
const res = await apiClient.post(mutateUrl, { action: "comment", id: a.id, body: a.body }, { headers });
|
|
40582
|
+
if (!res.success) {
|
|
40583
|
+
lines.push(`- [err] issue.comment ${a.id}: ${res.error || res.status}`);
|
|
40584
|
+
tally(false);
|
|
40585
|
+
continue;
|
|
40586
|
+
}
|
|
40587
|
+
const dispatched = res.data?.dispatched;
|
|
40588
|
+
lines.push(`- [ok] commented on ${a.id}${dispatched ? " -> dispatched agent" : ""}`);
|
|
40589
|
+
tally(true);
|
|
40562
40590
|
} else if (a.type === "issue.delete") {
|
|
40563
40591
|
const res = await apiClient.post(mutateUrl, { action: "delete", id: a.id }, { headers });
|
|
40564
40592
|
if (!res.success) {
|
|
@@ -40714,7 +40742,8 @@ var init_chat_plan_actions = __esm(() => {
|
|
|
40714
40742
|
"skill.attach": 5,
|
|
40715
40743
|
"skill.detach": 5,
|
|
40716
40744
|
"agent.update": 5,
|
|
40717
|
-
"session.create": 3
|
|
40745
|
+
"session.create": 3,
|
|
40746
|
+
"issue.comment": 5
|
|
40718
40747
|
};
|
|
40719
40748
|
});
|
|
40720
40749
|
|
|
@@ -40826,8 +40855,10 @@ class ChatSupervisor {
|
|
|
40826
40855
|
return;
|
|
40827
40856
|
}
|
|
40828
40857
|
}
|
|
40829
|
-
async resolveAgentRuntime(agentId) {
|
|
40830
|
-
if (
|
|
40858
|
+
async resolveAgentRuntime(chat2, agentId) {
|
|
40859
|
+
if (chat2.runtime)
|
|
40860
|
+
return chat2.runtime;
|
|
40861
|
+
if (!agentId || typeof agentId !== "string")
|
|
40831
40862
|
return null;
|
|
40832
40863
|
const headers = { authorization: `Bearer ${this.opts.authToken}` };
|
|
40833
40864
|
try {
|
|
@@ -40866,7 +40897,7 @@ class ChatSupervisor {
|
|
|
40866
40897
|
const mentionAgent = this.resolveAgentFromMention(allAgents, mentioned);
|
|
40867
40898
|
const resolvedAgentId = mentionAgent?.id ?? chat2.primary_agent_id ?? null;
|
|
40868
40899
|
const agentAuthorId = resolvedAgentId || mentioned || "agent";
|
|
40869
|
-
const runtime4 = await this.resolveAgentRuntime(resolvedAgentId);
|
|
40900
|
+
const runtime4 = await this.resolveAgentRuntime(chat2, resolvedAgentId);
|
|
40870
40901
|
const cwd = await this.resolveCwd(chat2);
|
|
40871
40902
|
if (cwd)
|
|
40872
40903
|
this.opts.log(`[chat ${chat2.id}] cwd=${cwd}`);
|
|
@@ -40894,7 +40925,7 @@ class ChatSupervisor {
|
|
|
40894
40925
|
peer.finalizePartialMessage(textContainerId);
|
|
40895
40926
|
textContainerId = null;
|
|
40896
40927
|
};
|
|
40897
|
-
const agentDisplayName = mentionAgent?.name ?? allAgents.find((a) => a.id === resolvedAgentId)?.name ?? undefined;
|
|
40928
|
+
const agentDisplayName = mentionAgent?.name ?? allAgents.find((a) => a.id === resolvedAgentId)?.name ?? runtime4 ?? undefined;
|
|
40898
40929
|
const ensureTextOpen = () => {
|
|
40899
40930
|
if (textContainerId == null) {
|
|
40900
40931
|
textContainerId = peer.beginPartialAgentMessage(agentAuthorId, agentDisplayName).containerId;
|
|
@@ -41064,7 +41095,7 @@ class ChatSupervisor {
|
|
|
41064
41095
|
}
|
|
41065
41096
|
}
|
|
41066
41097
|
async autoTitle(chat2, userMsg, agentReply) {
|
|
41067
|
-
const runtime4 = await this.resolveAgentRuntime(chat2);
|
|
41098
|
+
const runtime4 = await this.resolveAgentRuntime(chat2, chat2.primary_agent_id);
|
|
41068
41099
|
const trimUser = userMsg.slice(0, 800);
|
|
41069
41100
|
const trimAgent = agentReply.slice(0, 1200);
|
|
41070
41101
|
const prompt = `Reply with ONLY a 3-6 word title for this conversation. No quotes, no punctuation, no explanation. Capitalize each word.
|
|
@@ -41126,6 +41157,7 @@ Action vocabulary:
|
|
|
41126
41157
|
{"type":"update","id":"<issue id or key>","status":"done"},
|
|
41127
41158
|
{"type":"delegate","id":"<issue id>","assignee_id":"<agent id>"},
|
|
41128
41159
|
{"type":"handoff","target_agent_id":"<agent id>","prompt":"..."},
|
|
41160
|
+
{"type":"issue.comment","id":"<issue id or key>","body":"@<agent name> ..."},
|
|
41129
41161
|
{"type":"issue.delete","id":"<issue id>"},
|
|
41130
41162
|
{"type":"issue.delete_where","status":"todo","limit":50},
|
|
41131
41163
|
{"type":"issue.list","status":"todo","limit":20},
|
|
@@ -41140,8 +41172,9 @@ Action vocabulary:
|
|
|
41140
41172
|
|
|
41141
41173
|
Rules:
|
|
41142
41174
|
- Omit the block entirely if no actions are needed. Don't emit empty arrays.
|
|
41143
|
-
- Max 10 actions per turn. Sub-caps: agent.create=2, skill.create=3, agent.update=5, skill.attach/detach=5, session.create=3.
|
|
41175
|
+
- Max 10 actions per turn. Sub-caps: agent.create=2, skill.create=3, agent.update=5, skill.attach/detach=5, session.create=3, issue.comment=5.
|
|
41144
41176
|
- Chat-initiated agent.create / agent.update / skill.attach are auto-approved (the user is reading this reply right now). skill.create still queues for human review.
|
|
41177
|
+
- Use \`issue.comment\` with \`@<agent name>\` mention to dispatch an agent on an existing issue. Plain comments without an @mention are recorded but do not trigger a run. Issues whose autonomy is \`manual\` will not dispatch.
|
|
41145
41178
|
- \`allowed_tools\` on a new agent must be a subset of generally available tools.
|
|
41146
41179
|
|
|
41147
41180
|
To draw an inline panel for the user, append a separate fenced \`multi-ui\` block. The host uploads it to a Cloudflare Worker (24h TTL), serves the rendered HTML at a public URL, and embeds it in a sandboxed iframe (\`allow-scripts allow-forms\`). No reactive runtime — write plain HTML with optional inline \`<script>\` for interactivity. \`window.data\` inside your script is the JSON you sent.
|
|
@@ -41730,7 +41763,7 @@ import { parseArgs } from "util";
|
|
|
41730
41763
|
// package.json
|
|
41731
41764
|
var package_default = {
|
|
41732
41765
|
name: "@shipers-dev/multi",
|
|
41733
|
-
version: "0.
|
|
41766
|
+
version: "0.39.1",
|
|
41734
41767
|
type: "module",
|
|
41735
41768
|
bin: {
|
|
41736
41769
|
"multi-agent": "./dist/index.js"
|
|
@@ -42547,7 +42580,133 @@ var linkCmd = exports_Effect.fn("linkCmd")(function* (apiUrl, agentId) {
|
|
|
42547
42580
|
// src/commands/restart.ts
|
|
42548
42581
|
init_esm();
|
|
42549
42582
|
init_paths();
|
|
42550
|
-
import { existsSync as
|
|
42583
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8, unlinkSync as unlinkSync4 } from "fs";
|
|
42584
|
+
|
|
42585
|
+
// src/_impl/pid-lock.ts
|
|
42586
|
+
import { closeSync, existsSync as existsSync7, mkdirSync as mkdirSync6, openSync, readFileSync as readFileSync7, unlinkSync as unlinkSync3, writeSync } from "fs";
|
|
42587
|
+
import { dirname as dirname8 } from "path";
|
|
42588
|
+
var isAlive = (pid) => {
|
|
42589
|
+
if (!Number.isFinite(pid) || pid <= 0)
|
|
42590
|
+
return false;
|
|
42591
|
+
try {
|
|
42592
|
+
process.kill(pid, 0);
|
|
42593
|
+
return true;
|
|
42594
|
+
} catch (e) {
|
|
42595
|
+
return e?.code === "EPERM";
|
|
42596
|
+
}
|
|
42597
|
+
};
|
|
42598
|
+
var writeExclusive = (path, pid) => {
|
|
42599
|
+
mkdirSync6(dirname8(path), { recursive: true });
|
|
42600
|
+
const fd = openSync(path, "wx");
|
|
42601
|
+
try {
|
|
42602
|
+
writeSync(fd, String(pid));
|
|
42603
|
+
} finally {
|
|
42604
|
+
closeSync(fd);
|
|
42605
|
+
}
|
|
42606
|
+
};
|
|
42607
|
+
var acquirePidLock = (path) => {
|
|
42608
|
+
const ourPid = process.pid;
|
|
42609
|
+
try {
|
|
42610
|
+
writeExclusive(path, ourPid);
|
|
42611
|
+
return { ok: true };
|
|
42612
|
+
} catch (e) {
|
|
42613
|
+
if (e?.code !== "EEXIST")
|
|
42614
|
+
throw e;
|
|
42615
|
+
}
|
|
42616
|
+
const raw = (() => {
|
|
42617
|
+
try {
|
|
42618
|
+
return readFileSync7(path, "utf8").trim();
|
|
42619
|
+
} catch {
|
|
42620
|
+
return "";
|
|
42621
|
+
}
|
|
42622
|
+
})();
|
|
42623
|
+
const existingPid = Number(raw);
|
|
42624
|
+
if (existingPid === ourPid)
|
|
42625
|
+
return { ok: true };
|
|
42626
|
+
if (Number.isFinite(existingPid) && existingPid > 0 && isAlive(existingPid)) {
|
|
42627
|
+
return { ok: false, existingPid };
|
|
42628
|
+
}
|
|
42629
|
+
try {
|
|
42630
|
+
unlinkSync3(path);
|
|
42631
|
+
} catch {}
|
|
42632
|
+
try {
|
|
42633
|
+
writeExclusive(path, ourPid);
|
|
42634
|
+
return { ok: true };
|
|
42635
|
+
} catch (e) {
|
|
42636
|
+
if (e?.code !== "EEXIST")
|
|
42637
|
+
throw e;
|
|
42638
|
+
const racingRaw = (() => {
|
|
42639
|
+
try {
|
|
42640
|
+
return readFileSync7(path, "utf8").trim();
|
|
42641
|
+
} catch {
|
|
42642
|
+
return "";
|
|
42643
|
+
}
|
|
42644
|
+
})();
|
|
42645
|
+
const racingPid = Number(racingRaw);
|
|
42646
|
+
return { ok: false, existingPid: Number.isFinite(racingPid) ? racingPid : 0 };
|
|
42647
|
+
}
|
|
42648
|
+
};
|
|
42649
|
+
var releasePidLock = (path) => {
|
|
42650
|
+
try {
|
|
42651
|
+
if (!existsSync7(path))
|
|
42652
|
+
return;
|
|
42653
|
+
const raw = readFileSync7(path, "utf8").trim();
|
|
42654
|
+
if (Number(raw) !== process.pid)
|
|
42655
|
+
return;
|
|
42656
|
+
unlinkSync3(path);
|
|
42657
|
+
} catch {}
|
|
42658
|
+
};
|
|
42659
|
+
var killStaleConnects = () => {
|
|
42660
|
+
const self2 = process.pid;
|
|
42661
|
+
const parent = process.ppid;
|
|
42662
|
+
const out = (() => {
|
|
42663
|
+
try {
|
|
42664
|
+
const r = Bun.spawnSync(["ps", "-A", "-o", "pid=,command="], { stderr: "ignore" });
|
|
42665
|
+
return new TextDecoder().decode(r.stdout);
|
|
42666
|
+
} catch {
|
|
42667
|
+
return "";
|
|
42668
|
+
}
|
|
42669
|
+
})();
|
|
42670
|
+
const victims = [];
|
|
42671
|
+
for (const line of out.split(`
|
|
42672
|
+
`)) {
|
|
42673
|
+
const m = line.trim().match(/^(\d+)\s+(.*)$/);
|
|
42674
|
+
if (!m)
|
|
42675
|
+
continue;
|
|
42676
|
+
const pid = Number(m[1]);
|
|
42677
|
+
const cmd = m[2];
|
|
42678
|
+
if (!Number.isFinite(pid) || pid === self2 || pid === parent)
|
|
42679
|
+
continue;
|
|
42680
|
+
if (!/@shipers-dev\/multi\/dist\/index\.js/.test(cmd) && !/multi-agent/.test(cmd))
|
|
42681
|
+
continue;
|
|
42682
|
+
if (!/\bconnect\b/.test(cmd))
|
|
42683
|
+
continue;
|
|
42684
|
+
victims.push(pid);
|
|
42685
|
+
}
|
|
42686
|
+
for (const pid of victims) {
|
|
42687
|
+
try {
|
|
42688
|
+
process.kill(pid, "SIGTERM");
|
|
42689
|
+
} catch {}
|
|
42690
|
+
}
|
|
42691
|
+
if (victims.length === 0)
|
|
42692
|
+
return victims;
|
|
42693
|
+
const deadline = Date.now() + 1500;
|
|
42694
|
+
while (Date.now() < deadline) {
|
|
42695
|
+
if (!victims.some((p) => isAlive(p)))
|
|
42696
|
+
break;
|
|
42697
|
+
Bun.sleepSync(100);
|
|
42698
|
+
}
|
|
42699
|
+
for (const pid of victims) {
|
|
42700
|
+
if (isAlive(pid)) {
|
|
42701
|
+
try {
|
|
42702
|
+
process.kill(pid, "SIGKILL");
|
|
42703
|
+
} catch {}
|
|
42704
|
+
}
|
|
42705
|
+
}
|
|
42706
|
+
return victims;
|
|
42707
|
+
};
|
|
42708
|
+
|
|
42709
|
+
// src/commands/restart.ts
|
|
42551
42710
|
var isRunning4 = (pid) => {
|
|
42552
42711
|
try {
|
|
42553
42712
|
process.kill(pid, 0);
|
|
@@ -42559,8 +42718,8 @@ var isRunning4 = (pid) => {
|
|
|
42559
42718
|
var restartCmd = exports_Effect.fn("restartCmd")(function* (apiUrl) {
|
|
42560
42719
|
const fs3 = yield* FileSystem;
|
|
42561
42720
|
yield* (yield* Config4).ensureDirs;
|
|
42562
|
-
if (
|
|
42563
|
-
const pid = Number(
|
|
42721
|
+
if (existsSync8(PID_PATH)) {
|
|
42722
|
+
const pid = Number(readFileSync8(PID_PATH, "utf8").trim());
|
|
42564
42723
|
if (pid && isRunning4(pid)) {
|
|
42565
42724
|
yield* fs3.writeText(STOP_PATH, "1");
|
|
42566
42725
|
try {
|
|
@@ -42579,14 +42738,17 @@ var restartCmd = exports_Effect.fn("restartCmd")(function* (apiUrl) {
|
|
|
42579
42738
|
}
|
|
42580
42739
|
}
|
|
42581
42740
|
try {
|
|
42582
|
-
if (
|
|
42583
|
-
|
|
42741
|
+
if (existsSync8(PID_PATH))
|
|
42742
|
+
unlinkSync4(PID_PATH);
|
|
42584
42743
|
} catch {}
|
|
42585
42744
|
}
|
|
42586
42745
|
try {
|
|
42587
|
-
if (
|
|
42588
|
-
|
|
42746
|
+
if (existsSync8(STOP_PATH))
|
|
42747
|
+
unlinkSync4(STOP_PATH);
|
|
42589
42748
|
} catch {}
|
|
42749
|
+
const killed = killStaleConnects();
|
|
42750
|
+
if (killed.length)
|
|
42751
|
+
console.log(`⏹ Killed stale daemons: ${killed.join(",")}`);
|
|
42590
42752
|
console.log("\uD83D\uDD04 Relaunching daemon...");
|
|
42591
42753
|
const args2 = Bun.argv.slice(1).filter((a) => a !== "-d" && a !== "--detach" && a !== "restart");
|
|
42592
42754
|
if (!args2.includes("connect"))
|
|
@@ -42678,7 +42840,7 @@ init_client();
|
|
|
42678
42840
|
init_detect();
|
|
42679
42841
|
init_run_task();
|
|
42680
42842
|
import { Database as Database3 } from "bun:sqlite";
|
|
42681
|
-
import { existsSync as
|
|
42843
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync10, writeFileSync as writeFileSync8, unlinkSync as unlinkSync6 } from "fs";
|
|
42682
42844
|
import { homedir as homedir2 } from "os";
|
|
42683
42845
|
import { join as join13 } from "path";
|
|
42684
42846
|
init_errors();
|
|
@@ -42688,11 +42850,11 @@ var PORT_PATH2 = join13(MULTI_DIR5, "agent.port");
|
|
|
42688
42850
|
var STOP_PATH2 = join13(MULTI_DIR5, "stop.flag");
|
|
42689
42851
|
var TASKS_DB_PATH3 = join13(MULTI_DIR5, "tasks.db");
|
|
42690
42852
|
function ensureDirs2() {
|
|
42691
|
-
if (!
|
|
42692
|
-
|
|
42853
|
+
if (!existsSync13(MULTI_DIR5))
|
|
42854
|
+
mkdirSync10(MULTI_DIR5, { recursive: true });
|
|
42693
42855
|
const logs = join13(MULTI_DIR5, "logs");
|
|
42694
|
-
if (!
|
|
42695
|
-
|
|
42856
|
+
if (!existsSync13(logs))
|
|
42857
|
+
mkdirSync10(logs, { recursive: true });
|
|
42696
42858
|
}
|
|
42697
42859
|
function isLocalApi(url2) {
|
|
42698
42860
|
try {
|
|
@@ -42871,8 +43033,8 @@ async function ackDispatch(apiUrl, wsId, dispatchId, secret) {
|
|
|
42871
43033
|
log3(`ack dispatch ${dispatchId} failed: ${String(e)}`);
|
|
42872
43034
|
}
|
|
42873
43035
|
}
|
|
42874
|
-
async function drainOfflineDispatches(apiUrl, deviceId, wsId, secret, db2, onEnqueued,
|
|
42875
|
-
if (!
|
|
43036
|
+
async function drainOfflineDispatches(apiUrl, deviceId, wsId, secret, db2, onEnqueued, isAlive2) {
|
|
43037
|
+
if (!isAlive2())
|
|
42876
43038
|
return;
|
|
42877
43039
|
let list;
|
|
42878
43040
|
try {
|
|
@@ -42886,7 +43048,7 @@ async function drainOfflineDispatches(apiUrl, deviceId, wsId, secret, db2, onEnq
|
|
|
42886
43048
|
return;
|
|
42887
43049
|
log3(`draining ${rows.length} offline dispatch(es)`);
|
|
42888
43050
|
for (const r of rows) {
|
|
42889
|
-
if (!
|
|
43051
|
+
if (!isAlive2()) {
|
|
42890
43052
|
log3("drain aborted: shutting down");
|
|
42891
43053
|
return;
|
|
42892
43054
|
}
|
|
@@ -42984,8 +43146,8 @@ var daemonProgram = ({ cfg, apiUrl }) => exports_Effect.gen(function* () {
|
|
|
42984
43146
|
if (cfg.authToken)
|
|
42985
43147
|
setAuthToken(cfg.authToken);
|
|
42986
43148
|
ensureDirs2();
|
|
42987
|
-
if (
|
|
42988
|
-
|
|
43149
|
+
if (existsSync13(STOP_PATH2))
|
|
43150
|
+
unlinkSync6(STOP_PATH2);
|
|
42989
43151
|
const detected = yield* exports_Effect.promise(() => detectAgents());
|
|
42990
43152
|
const localMode = isLocalApi(apiUrl);
|
|
42991
43153
|
log3(`daemon device=${cfg.deviceId} pid=${process.pid}`);
|
|
@@ -43147,7 +43309,7 @@ var daemonProgram = ({ cfg, apiUrl }) => exports_Effect.gen(function* () {
|
|
|
43147
43309
|
let probeFailures = 0;
|
|
43148
43310
|
while (true) {
|
|
43149
43311
|
yield* exports_Effect.sleep(exports_Duration.seconds(120));
|
|
43150
|
-
if (
|
|
43312
|
+
if (existsSync13(STOP_PATH2)) {
|
|
43151
43313
|
yield* exports_Deferred.succeed(stopDeferred, "stop flag");
|
|
43152
43314
|
return;
|
|
43153
43315
|
}
|
|
@@ -43172,7 +43334,7 @@ var daemonProgram = ({ cfg, apiUrl }) => exports_Effect.gen(function* () {
|
|
|
43172
43334
|
yield* exports_Effect.forkIn(daemonScope)(exports_Effect.gen(function* () {
|
|
43173
43335
|
while (true) {
|
|
43174
43336
|
yield* exports_Effect.sleep(exports_Duration.seconds(5));
|
|
43175
|
-
if (
|
|
43337
|
+
if (existsSync13(STOP_PATH2)) {
|
|
43176
43338
|
yield* exports_Deferred.succeed(stopDeferred, "stop flag");
|
|
43177
43339
|
return;
|
|
43178
43340
|
}
|
|
@@ -43209,12 +43371,12 @@ var daemonProgram = ({ cfg, apiUrl }) => exports_Effect.gen(function* () {
|
|
|
43209
43371
|
yield* exports_Effect.race(exports_Fiber.interruptAll(inFlight), exports_Effect.sleep(exports_Duration.seconds(10)));
|
|
43210
43372
|
}
|
|
43211
43373
|
yield* exports_Scope.close(daemonScope, exports_Exit.void).pipe(exports_Effect.catchAll(() => exports_Effect.void));
|
|
43212
|
-
if (
|
|
43213
|
-
|
|
43214
|
-
if (
|
|
43215
|
-
|
|
43216
|
-
if (
|
|
43217
|
-
|
|
43374
|
+
if (existsSync13(PID_PATH2))
|
|
43375
|
+
unlinkSync6(PID_PATH2);
|
|
43376
|
+
if (existsSync13(STOP_PATH2))
|
|
43377
|
+
unlinkSync6(STOP_PATH2);
|
|
43378
|
+
if (existsSync13(PORT_PATH2))
|
|
43379
|
+
unlinkSync6(PORT_PATH2);
|
|
43218
43380
|
db2.close();
|
|
43219
43381
|
log3("disconnected");
|
|
43220
43382
|
});
|
|
@@ -43226,81 +43388,6 @@ async function daemonMain(opts) {
|
|
|
43226
43388
|
}
|
|
43227
43389
|
}
|
|
43228
43390
|
|
|
43229
|
-
// src/_impl/pid-lock.ts
|
|
43230
|
-
import { closeSync, existsSync as existsSync13, mkdirSync as mkdirSync10, openSync, readFileSync as readFileSync10, unlinkSync as unlinkSync6, writeSync } from "fs";
|
|
43231
|
-
import { dirname as dirname10 } from "path";
|
|
43232
|
-
var isAlive = (pid) => {
|
|
43233
|
-
if (!Number.isFinite(pid) || pid <= 0)
|
|
43234
|
-
return false;
|
|
43235
|
-
try {
|
|
43236
|
-
process.kill(pid, 0);
|
|
43237
|
-
return true;
|
|
43238
|
-
} catch (e) {
|
|
43239
|
-
return e?.code === "EPERM";
|
|
43240
|
-
}
|
|
43241
|
-
};
|
|
43242
|
-
var writeExclusive = (path, pid) => {
|
|
43243
|
-
mkdirSync10(dirname10(path), { recursive: true });
|
|
43244
|
-
const fd = openSync(path, "wx");
|
|
43245
|
-
try {
|
|
43246
|
-
writeSync(fd, String(pid));
|
|
43247
|
-
} finally {
|
|
43248
|
-
closeSync(fd);
|
|
43249
|
-
}
|
|
43250
|
-
};
|
|
43251
|
-
var acquirePidLock = (path) => {
|
|
43252
|
-
const ourPid = process.pid;
|
|
43253
|
-
try {
|
|
43254
|
-
writeExclusive(path, ourPid);
|
|
43255
|
-
return { ok: true };
|
|
43256
|
-
} catch (e) {
|
|
43257
|
-
if (e?.code !== "EEXIST")
|
|
43258
|
-
throw e;
|
|
43259
|
-
}
|
|
43260
|
-
const raw = (() => {
|
|
43261
|
-
try {
|
|
43262
|
-
return readFileSync10(path, "utf8").trim();
|
|
43263
|
-
} catch {
|
|
43264
|
-
return "";
|
|
43265
|
-
}
|
|
43266
|
-
})();
|
|
43267
|
-
const existingPid = Number(raw);
|
|
43268
|
-
if (existingPid === ourPid)
|
|
43269
|
-
return { ok: true };
|
|
43270
|
-
if (Number.isFinite(existingPid) && existingPid > 0 && isAlive(existingPid)) {
|
|
43271
|
-
return { ok: false, existingPid };
|
|
43272
|
-
}
|
|
43273
|
-
try {
|
|
43274
|
-
unlinkSync6(path);
|
|
43275
|
-
} catch {}
|
|
43276
|
-
try {
|
|
43277
|
-
writeExclusive(path, ourPid);
|
|
43278
|
-
return { ok: true };
|
|
43279
|
-
} catch (e) {
|
|
43280
|
-
if (e?.code !== "EEXIST")
|
|
43281
|
-
throw e;
|
|
43282
|
-
const racingRaw = (() => {
|
|
43283
|
-
try {
|
|
43284
|
-
return readFileSync10(path, "utf8").trim();
|
|
43285
|
-
} catch {
|
|
43286
|
-
return "";
|
|
43287
|
-
}
|
|
43288
|
-
})();
|
|
43289
|
-
const racingPid = Number(racingRaw);
|
|
43290
|
-
return { ok: false, existingPid: Number.isFinite(racingPid) ? racingPid : 0 };
|
|
43291
|
-
}
|
|
43292
|
-
};
|
|
43293
|
-
var releasePidLock = (path) => {
|
|
43294
|
-
try {
|
|
43295
|
-
if (!existsSync13(path))
|
|
43296
|
-
return;
|
|
43297
|
-
const raw = readFileSync10(path, "utf8").trim();
|
|
43298
|
-
if (Number(raw) !== process.pid)
|
|
43299
|
-
return;
|
|
43300
|
-
unlinkSync6(path);
|
|
43301
|
-
} catch {}
|
|
43302
|
-
};
|
|
43303
|
-
|
|
43304
43391
|
// src/commands/connect.ts
|
|
43305
43392
|
init_outbox();
|
|
43306
43393
|
init_paths();
|
|
@@ -43319,6 +43406,9 @@ var connectCmd = exports_Effect.fn("connectCmd")(function* () {
|
|
|
43319
43406
|
}
|
|
43320
43407
|
if (cfg.authToken)
|
|
43321
43408
|
yield* api2.setAuthToken(cfg.authToken);
|
|
43409
|
+
const killed = killStaleConnects();
|
|
43410
|
+
if (killed.length)
|
|
43411
|
+
yield* logger.log(`connect: killed stale daemons ${killed.join(",")}`);
|
|
43322
43412
|
if (process.env.MULTI_FORCE_CONNECT !== "1") {
|
|
43323
43413
|
const lock = acquirePidLock(PID_PATH);
|
|
43324
43414
|
if (!lock.ok) {
|