@openacp/cli 2026.408.3 → 2026.410.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/cli.d.ts +0 -1
- package/dist/cli.js +2282 -1749
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +17 -20
- package/dist/index.js +1161 -932
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -198,11 +198,11 @@ async function shutdownLogger() {
|
|
|
198
198
|
logDir = void 0;
|
|
199
199
|
initialized = false;
|
|
200
200
|
if (transport) {
|
|
201
|
-
await new Promise((
|
|
202
|
-
const timeout = setTimeout(
|
|
201
|
+
await new Promise((resolve7) => {
|
|
202
|
+
const timeout = setTimeout(resolve7, 3e3);
|
|
203
203
|
transport.on("close", () => {
|
|
204
204
|
clearTimeout(timeout);
|
|
205
|
-
|
|
205
|
+
resolve7();
|
|
206
206
|
});
|
|
207
207
|
transport.end();
|
|
208
208
|
});
|
|
@@ -245,6 +245,9 @@ var init_log = __esm({
|
|
|
245
245
|
});
|
|
246
246
|
|
|
247
247
|
// src/core/config/config-migrations.ts
|
|
248
|
+
import fs2 from "fs";
|
|
249
|
+
import path2 from "path";
|
|
250
|
+
import os2 from "os";
|
|
248
251
|
function applyMigrations(raw, migrationList = migrations, ctx) {
|
|
249
252
|
let changed = false;
|
|
250
253
|
for (const migration of migrationList) {
|
|
@@ -278,75 +281,34 @@ var init_config_migrations = __esm({
|
|
|
278
281
|
log2.info("Removed legacy displayVerbosity key from config");
|
|
279
282
|
return true;
|
|
280
283
|
}
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
name: "add-instance-id",
|
|
287
|
+
apply(raw, ctx) {
|
|
288
|
+
if (raw.id) return false;
|
|
289
|
+
if (!ctx?.configDir) return false;
|
|
290
|
+
const instanceRoot = ctx.configDir;
|
|
291
|
+
try {
|
|
292
|
+
const registryPath = path2.join(os2.homedir(), ".openacp", "instances.json");
|
|
293
|
+
const data = JSON.parse(fs2.readFileSync(registryPath, "utf-8"));
|
|
294
|
+
const instances = data?.instances ?? {};
|
|
295
|
+
const entry = Object.values(instances).find(
|
|
296
|
+
(e) => e.root === instanceRoot
|
|
297
|
+
);
|
|
298
|
+
if (entry?.id) {
|
|
299
|
+
raw.id = entry.id;
|
|
300
|
+
log2.info({ instanceRoot }, "Migrated: added id to config from registry");
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
} catch {
|
|
304
|
+
}
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
281
307
|
}
|
|
282
308
|
];
|
|
283
309
|
}
|
|
284
310
|
});
|
|
285
311
|
|
|
286
|
-
// src/core/instance/instance-context.ts
|
|
287
|
-
var instance_context_exports = {};
|
|
288
|
-
__export(instance_context_exports, {
|
|
289
|
-
createInstanceContext: () => createInstanceContext,
|
|
290
|
-
generateSlug: () => generateSlug,
|
|
291
|
-
getGlobalRoot: () => getGlobalRoot,
|
|
292
|
-
resolveInstanceRoot: () => resolveInstanceRoot
|
|
293
|
-
});
|
|
294
|
-
import path2 from "path";
|
|
295
|
-
import fs2 from "fs";
|
|
296
|
-
import os2 from "os";
|
|
297
|
-
function createInstanceContext(opts) {
|
|
298
|
-
const { id, root, isGlobal } = opts;
|
|
299
|
-
return {
|
|
300
|
-
id,
|
|
301
|
-
root,
|
|
302
|
-
isGlobal,
|
|
303
|
-
paths: {
|
|
304
|
-
config: path2.join(root, "config.json"),
|
|
305
|
-
sessions: path2.join(root, "sessions.json"),
|
|
306
|
-
agents: path2.join(root, "agents.json"),
|
|
307
|
-
registryCache: path2.join(root, "registry-cache.json"),
|
|
308
|
-
plugins: path2.join(root, "plugins"),
|
|
309
|
-
pluginsData: path2.join(root, "plugins", "data"),
|
|
310
|
-
pluginRegistry: path2.join(root, "plugins.json"),
|
|
311
|
-
logs: path2.join(root, "logs"),
|
|
312
|
-
pid: path2.join(root, "openacp.pid"),
|
|
313
|
-
running: path2.join(root, "running"),
|
|
314
|
-
apiPort: path2.join(root, "api.port"),
|
|
315
|
-
apiSecret: path2.join(root, "api-secret"),
|
|
316
|
-
bin: path2.join(root, "bin"),
|
|
317
|
-
cache: path2.join(root, "cache"),
|
|
318
|
-
tunnels: path2.join(root, "tunnels.json"),
|
|
319
|
-
agentsDir: path2.join(root, "agents")
|
|
320
|
-
}
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
function generateSlug(name) {
|
|
324
|
-
const slug = name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
325
|
-
return slug || "openacp";
|
|
326
|
-
}
|
|
327
|
-
function expandHome2(p) {
|
|
328
|
-
if (p.startsWith("~")) return path2.join(os2.homedir(), p.slice(1));
|
|
329
|
-
return p;
|
|
330
|
-
}
|
|
331
|
-
function resolveInstanceRoot(opts) {
|
|
332
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
333
|
-
if (opts.dir) return path2.join(expandHome2(opts.dir), ".openacp");
|
|
334
|
-
if (opts.local) return path2.join(cwd, ".openacp");
|
|
335
|
-
if (opts.global) return path2.join(os2.homedir(), ".openacp");
|
|
336
|
-
const localRoot = path2.join(cwd, ".openacp");
|
|
337
|
-
if (fs2.existsSync(localRoot)) return localRoot;
|
|
338
|
-
if (process.env.OPENACP_INSTANCE_ROOT) return process.env.OPENACP_INSTANCE_ROOT;
|
|
339
|
-
return null;
|
|
340
|
-
}
|
|
341
|
-
function getGlobalRoot() {
|
|
342
|
-
return path2.join(os2.homedir(), ".openacp");
|
|
343
|
-
}
|
|
344
|
-
var init_instance_context = __esm({
|
|
345
|
-
"src/core/instance/instance-context.ts"() {
|
|
346
|
-
"use strict";
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
|
|
350
312
|
// src/core/config/config-registry.ts
|
|
351
313
|
var config_registry_exports = {};
|
|
352
314
|
__export(config_registry_exports, {
|
|
@@ -359,24 +321,22 @@ __export(config_registry_exports, {
|
|
|
359
321
|
resolveOptions: () => resolveOptions,
|
|
360
322
|
setFieldValueAsync: () => setFieldValueAsync
|
|
361
323
|
});
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
function getFieldDef(path34) {
|
|
365
|
-
return CONFIG_REGISTRY.find((f) => f.path === path34);
|
|
324
|
+
function getFieldDef(path35) {
|
|
325
|
+
return CONFIG_REGISTRY.find((f) => f.path === path35);
|
|
366
326
|
}
|
|
367
327
|
function getSafeFields() {
|
|
368
328
|
return CONFIG_REGISTRY.filter((f) => f.scope === "safe");
|
|
369
329
|
}
|
|
370
|
-
function isHotReloadable(
|
|
371
|
-
const def = getFieldDef(
|
|
330
|
+
function isHotReloadable(path35) {
|
|
331
|
+
const def = getFieldDef(path35);
|
|
372
332
|
return def?.hotReload ?? false;
|
|
373
333
|
}
|
|
374
334
|
function resolveOptions(def, config) {
|
|
375
335
|
if (!def.options) return void 0;
|
|
376
336
|
return typeof def.options === "function" ? def.options(config) : def.options;
|
|
377
337
|
}
|
|
378
|
-
function getConfigValue(config,
|
|
379
|
-
const parts =
|
|
338
|
+
function getConfigValue(config, path35) {
|
|
339
|
+
const parts = path35.split(".");
|
|
380
340
|
let current = config;
|
|
381
341
|
for (const part of parts) {
|
|
382
342
|
if (current && typeof current === "object" && part in current) {
|
|
@@ -421,7 +381,6 @@ var CONFIG_REGISTRY, ConfigValidationError;
|
|
|
421
381
|
var init_config_registry = __esm({
|
|
422
382
|
"src/core/config/config-registry.ts"() {
|
|
423
383
|
"use strict";
|
|
424
|
-
init_instance_context();
|
|
425
384
|
CONFIG_REGISTRY = [
|
|
426
385
|
{
|
|
427
386
|
path: "defaultAgent",
|
|
@@ -429,17 +388,8 @@ var init_config_registry = __esm({
|
|
|
429
388
|
group: "agent",
|
|
430
389
|
type: "select",
|
|
431
390
|
options: (config) => {
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
try {
|
|
435
|
-
const agentsPath = path3.join(getGlobalRoot(), "agents.json");
|
|
436
|
-
if (fs3.existsSync(agentsPath)) {
|
|
437
|
-
const data = JSON.parse(fs3.readFileSync(agentsPath, "utf-8"));
|
|
438
|
-
return Object.keys(data.installed ?? {});
|
|
439
|
-
}
|
|
440
|
-
} catch {
|
|
441
|
-
}
|
|
442
|
-
return [];
|
|
391
|
+
const current = config.defaultAgent;
|
|
392
|
+
return current ? [current] : [];
|
|
443
393
|
},
|
|
444
394
|
scope: "safe",
|
|
445
395
|
hotReload: true
|
|
@@ -453,14 +403,6 @@ var init_config_registry = __esm({
|
|
|
453
403
|
scope: "safe",
|
|
454
404
|
hotReload: true
|
|
455
405
|
},
|
|
456
|
-
{
|
|
457
|
-
path: "workspace.baseDir",
|
|
458
|
-
displayName: "Workspace Directory",
|
|
459
|
-
group: "workspace",
|
|
460
|
-
type: "string",
|
|
461
|
-
scope: "safe",
|
|
462
|
-
hotReload: true
|
|
463
|
-
},
|
|
464
406
|
{
|
|
465
407
|
path: "sessionStore.ttlDays",
|
|
466
408
|
displayName: "Session Store TTL (days)",
|
|
@@ -489,14 +431,14 @@ var init_config_registry = __esm({
|
|
|
489
431
|
|
|
490
432
|
// src/core/config/config.ts
|
|
491
433
|
import { z } from "zod";
|
|
492
|
-
import * as
|
|
493
|
-
import * as
|
|
434
|
+
import * as fs3 from "fs";
|
|
435
|
+
import * as path3 from "path";
|
|
494
436
|
import * as os3 from "os";
|
|
495
437
|
import { randomBytes } from "crypto";
|
|
496
438
|
import { EventEmitter } from "events";
|
|
497
|
-
function
|
|
439
|
+
function expandHome2(p) {
|
|
498
440
|
if (p.startsWith("~")) {
|
|
499
|
-
return
|
|
441
|
+
return path3.join(os3.homedir(), p.slice(1));
|
|
500
442
|
}
|
|
501
443
|
return p;
|
|
502
444
|
}
|
|
@@ -515,10 +457,11 @@ var init_config = __esm({
|
|
|
515
457
|
sessionLogRetentionDays: z.number().default(30)
|
|
516
458
|
}).default({});
|
|
517
459
|
ConfigSchema = z.object({
|
|
460
|
+
id: z.string().optional(),
|
|
461
|
+
// instance UUID, written once at creation time
|
|
518
462
|
instanceName: z.string().optional(),
|
|
519
463
|
defaultAgent: z.string(),
|
|
520
464
|
workspace: z.object({
|
|
521
|
-
baseDir: z.string().default("~/openacp-workspace"),
|
|
522
465
|
allowExternalWorkspaces: z.boolean().default(true),
|
|
523
466
|
security: z.object({
|
|
524
467
|
allowedPaths: z.array(z.string()).default([]),
|
|
@@ -545,7 +488,6 @@ var init_config = __esm({
|
|
|
545
488
|
});
|
|
546
489
|
DEFAULT_CONFIG = {
|
|
547
490
|
defaultAgent: "claude",
|
|
548
|
-
workspace: { baseDir: "~/openacp-workspace" },
|
|
549
491
|
sessionStore: { ttlDays: 30 }
|
|
550
492
|
};
|
|
551
493
|
ConfigManager = class extends EventEmitter {
|
|
@@ -553,13 +495,13 @@ var init_config = __esm({
|
|
|
553
495
|
configPath;
|
|
554
496
|
constructor(configPath) {
|
|
555
497
|
super();
|
|
556
|
-
this.configPath = process.env.OPENACP_CONFIG_PATH || configPath ||
|
|
498
|
+
this.configPath = process.env.OPENACP_CONFIG_PATH || configPath || expandHome2("~/.openacp/config.json");
|
|
557
499
|
}
|
|
558
500
|
async load() {
|
|
559
|
-
const dir =
|
|
560
|
-
|
|
561
|
-
if (!
|
|
562
|
-
|
|
501
|
+
const dir = path3.dirname(this.configPath);
|
|
502
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
503
|
+
if (!fs3.existsSync(this.configPath)) {
|
|
504
|
+
fs3.writeFileSync(
|
|
563
505
|
this.configPath,
|
|
564
506
|
JSON.stringify(DEFAULT_CONFIG, null, 2)
|
|
565
507
|
);
|
|
@@ -569,10 +511,10 @@ var init_config = __esm({
|
|
|
569
511
|
);
|
|
570
512
|
process.exit(1);
|
|
571
513
|
}
|
|
572
|
-
const raw = JSON.parse(
|
|
573
|
-
const { changed: configUpdated } = applyMigrations(raw, void 0, { configDir:
|
|
514
|
+
const raw = JSON.parse(fs3.readFileSync(this.configPath, "utf-8"));
|
|
515
|
+
const { changed: configUpdated } = applyMigrations(raw, void 0, { configDir: path3.dirname(this.configPath) });
|
|
574
516
|
if (configUpdated) {
|
|
575
|
-
|
|
517
|
+
fs3.writeFileSync(this.configPath, JSON.stringify(raw, null, 2));
|
|
576
518
|
}
|
|
577
519
|
this.applyEnvOverrides(raw);
|
|
578
520
|
const result = ConfigSchema.safeParse(raw);
|
|
@@ -593,7 +535,7 @@ var init_config = __esm({
|
|
|
593
535
|
}
|
|
594
536
|
async save(updates, changePath) {
|
|
595
537
|
const oldConfig = this.config ? structuredClone(this.config) : void 0;
|
|
596
|
-
const raw = JSON.parse(
|
|
538
|
+
const raw = JSON.parse(fs3.readFileSync(this.configPath, "utf-8"));
|
|
597
539
|
this.deepMerge(raw, updates);
|
|
598
540
|
const result = ConfigSchema.safeParse(raw);
|
|
599
541
|
if (!result.success) {
|
|
@@ -601,8 +543,8 @@ var init_config = __esm({
|
|
|
601
543
|
return;
|
|
602
544
|
}
|
|
603
545
|
const tmpPath = this.configPath + `.tmp.${randomBytes(4).toString("hex")}`;
|
|
604
|
-
|
|
605
|
-
|
|
546
|
+
fs3.writeFileSync(tmpPath, JSON.stringify(raw, null, 2), "utf-8");
|
|
547
|
+
fs3.renameSync(tmpPath, this.configPath);
|
|
606
548
|
this.config = result.data;
|
|
607
549
|
if (changePath) {
|
|
608
550
|
const { getConfigValue: getConfigValue2 } = await Promise.resolve().then(() => (init_config_registry(), config_registry_exports));
|
|
@@ -632,54 +574,49 @@ var init_config = __esm({
|
|
|
632
574
|
await this.save(updates, dotPath);
|
|
633
575
|
}
|
|
634
576
|
resolveWorkspace(input2) {
|
|
577
|
+
const workspaceBase = path3.dirname(path3.dirname(this.configPath));
|
|
635
578
|
if (!input2) {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
if (
|
|
641
|
-
const
|
|
642
|
-
const base =
|
|
643
|
-
const isInternal =
|
|
579
|
+
fs3.mkdirSync(workspaceBase, { recursive: true });
|
|
580
|
+
return workspaceBase;
|
|
581
|
+
}
|
|
582
|
+
const expanded = input2.startsWith("~") ? expandHome2(input2) : input2;
|
|
583
|
+
if (path3.isAbsolute(expanded)) {
|
|
584
|
+
const resolved = path3.resolve(expanded);
|
|
585
|
+
const base = path3.resolve(workspaceBase);
|
|
586
|
+
const isInternal = resolved === base || resolved.startsWith(base + path3.sep);
|
|
644
587
|
if (!isInternal) {
|
|
645
588
|
if (!this.config.workspace.allowExternalWorkspaces) {
|
|
646
589
|
throw new Error(
|
|
647
|
-
`Workspace path "${input2}" is outside base directory "${
|
|
590
|
+
`Workspace path "${input2}" is outside base directory "${workspaceBase}". Set allowExternalWorkspaces: true to allow this.`
|
|
648
591
|
);
|
|
649
592
|
}
|
|
650
|
-
if (!
|
|
651
|
-
throw new Error(
|
|
652
|
-
`Workspace path "${input2}" does not exist.`
|
|
653
|
-
);
|
|
593
|
+
if (!fs3.existsSync(resolved)) {
|
|
594
|
+
throw new Error(`Workspace path "${resolved}" does not exist.`);
|
|
654
595
|
}
|
|
655
|
-
return
|
|
596
|
+
return resolved;
|
|
656
597
|
}
|
|
657
|
-
|
|
658
|
-
return
|
|
598
|
+
fs3.mkdirSync(resolved, { recursive: true });
|
|
599
|
+
return resolved;
|
|
659
600
|
}
|
|
660
|
-
|
|
661
|
-
if (name !== input2) {
|
|
601
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(input2)) {
|
|
662
602
|
throw new Error(
|
|
663
603
|
`Invalid workspace name: "${input2}". Only alphanumeric characters, hyphens, and underscores are allowed.`
|
|
664
604
|
);
|
|
665
605
|
}
|
|
666
|
-
const
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
);
|
|
670
|
-
fs4.mkdirSync(resolved, { recursive: true });
|
|
671
|
-
return resolved;
|
|
606
|
+
const namedPath = path3.join(workspaceBase, input2.toLowerCase());
|
|
607
|
+
fs3.mkdirSync(namedPath, { recursive: true });
|
|
608
|
+
return namedPath;
|
|
672
609
|
}
|
|
673
610
|
async exists() {
|
|
674
|
-
return
|
|
611
|
+
return fs3.existsSync(this.configPath);
|
|
675
612
|
}
|
|
676
613
|
getConfigPath() {
|
|
677
614
|
return this.configPath;
|
|
678
615
|
}
|
|
679
616
|
async writeNew(config) {
|
|
680
|
-
const dir =
|
|
681
|
-
|
|
682
|
-
|
|
617
|
+
const dir = path3.dirname(this.configPath);
|
|
618
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
619
|
+
fs3.writeFileSync(this.configPath, JSON.stringify(config, null, 2));
|
|
683
620
|
}
|
|
684
621
|
async applyEnvToPluginSettings(settingsManager) {
|
|
685
622
|
const pluginOverrides = [
|
|
@@ -762,9 +699,9 @@ var read_text_file_exports = {};
|
|
|
762
699
|
__export(read_text_file_exports, {
|
|
763
700
|
readTextFileWithRange: () => readTextFileWithRange
|
|
764
701
|
});
|
|
765
|
-
import
|
|
702
|
+
import fs5 from "fs";
|
|
766
703
|
async function readTextFileWithRange(filePath, options) {
|
|
767
|
-
const content = await
|
|
704
|
+
const content = await fs5.promises.readFile(filePath, "utf-8");
|
|
768
705
|
if (!options?.line && !options?.limit) return content;
|
|
769
706
|
const lines = content.split("\n");
|
|
770
707
|
const start = Math.max(0, (options.line ?? 1) - 1);
|
|
@@ -875,6 +812,25 @@ var init_events = __esm({
|
|
|
875
812
|
}
|
|
876
813
|
});
|
|
877
814
|
|
|
815
|
+
// src/core/utils/apply-patch-detection.ts
|
|
816
|
+
function asRecord(value) {
|
|
817
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
818
|
+
}
|
|
819
|
+
function hasApplyPatchPatchText(rawInput) {
|
|
820
|
+
const input2 = asRecord(rawInput);
|
|
821
|
+
return !!input2 && (typeof input2.patchText === "string" || typeof input2.patch_text === "string");
|
|
822
|
+
}
|
|
823
|
+
function isApplyPatchOtherTool(kind, name, rawInput) {
|
|
824
|
+
if (kind !== "other") return false;
|
|
825
|
+
if (name.toLowerCase() === "apply_patch") return true;
|
|
826
|
+
return hasApplyPatchPatchText(rawInput);
|
|
827
|
+
}
|
|
828
|
+
var init_apply_patch_detection = __esm({
|
|
829
|
+
"src/core/utils/apply-patch-detection.ts"() {
|
|
830
|
+
"use strict";
|
|
831
|
+
}
|
|
832
|
+
});
|
|
833
|
+
|
|
878
834
|
// src/core/utils/bypass-detection.ts
|
|
879
835
|
function isPermissionBypass(value) {
|
|
880
836
|
const lower = value.toLowerCase();
|
|
@@ -902,8 +858,8 @@ __export(agent_dependencies_exports, {
|
|
|
902
858
|
listAgentsWithIntegration: () => listAgentsWithIntegration
|
|
903
859
|
});
|
|
904
860
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
905
|
-
import * as
|
|
906
|
-
import * as
|
|
861
|
+
import * as fs10 from "fs";
|
|
862
|
+
import * as path9 from "path";
|
|
907
863
|
function getAgentSetup(registryId) {
|
|
908
864
|
return AGENT_SETUP[registryId];
|
|
909
865
|
}
|
|
@@ -927,9 +883,9 @@ function commandExists(cmd) {
|
|
|
927
883
|
}
|
|
928
884
|
let dir = process.cwd();
|
|
929
885
|
while (true) {
|
|
930
|
-
const binPath =
|
|
931
|
-
if (
|
|
932
|
-
const parent =
|
|
886
|
+
const binPath = path9.join(dir, "node_modules", ".bin", cmd);
|
|
887
|
+
if (fs10.existsSync(binPath)) return true;
|
|
888
|
+
const parent = path9.dirname(dir);
|
|
933
889
|
if (parent === dir) break;
|
|
934
890
|
dir = parent;
|
|
935
891
|
}
|
|
@@ -1202,9 +1158,9 @@ var init_agent_registry = __esm({
|
|
|
1202
1158
|
});
|
|
1203
1159
|
|
|
1204
1160
|
// src/core/agents/agent-installer.ts
|
|
1205
|
-
import * as
|
|
1206
|
-
import * as
|
|
1207
|
-
import * as
|
|
1161
|
+
import * as fs11 from "fs";
|
|
1162
|
+
import * as path10 from "path";
|
|
1163
|
+
import * as os4 from "os";
|
|
1208
1164
|
import crypto from "crypto";
|
|
1209
1165
|
function validateArchiveContents(entries, destDir) {
|
|
1210
1166
|
for (const entry of entries) {
|
|
@@ -1218,9 +1174,9 @@ function validateArchiveContents(entries, destDir) {
|
|
|
1218
1174
|
}
|
|
1219
1175
|
}
|
|
1220
1176
|
function validateUninstallPath(binaryPath, agentsDir) {
|
|
1221
|
-
const realPath =
|
|
1222
|
-
const realAgentsDir =
|
|
1223
|
-
if (!realPath.startsWith(realAgentsDir +
|
|
1177
|
+
const realPath = path10.resolve(binaryPath);
|
|
1178
|
+
const realAgentsDir = path10.resolve(agentsDir);
|
|
1179
|
+
if (!realPath.startsWith(realAgentsDir + path10.sep) && realPath !== realAgentsDir) {
|
|
1224
1180
|
throw new Error(`Refusing to delete path outside agents directory: ${realPath}`);
|
|
1225
1181
|
}
|
|
1226
1182
|
}
|
|
@@ -1274,7 +1230,7 @@ function buildInstalledAgent(registryId, name, version, dist, binaryPath) {
|
|
|
1274
1230
|
binaryPath: null
|
|
1275
1231
|
};
|
|
1276
1232
|
}
|
|
1277
|
-
const absCmd =
|
|
1233
|
+
const absCmd = path10.resolve(binaryPath, dist.cmd);
|
|
1278
1234
|
return {
|
|
1279
1235
|
registryId,
|
|
1280
1236
|
name,
|
|
@@ -1340,10 +1296,10 @@ Install it with: pip install uv`;
|
|
|
1340
1296
|
return { ok: true, agentKey, setupSteps: setupSteps.length > 0 ? setupSteps : void 0 };
|
|
1341
1297
|
}
|
|
1342
1298
|
async function downloadAndExtract(agentId, archiveUrl, progress, agentsDir) {
|
|
1343
|
-
const destDir =
|
|
1344
|
-
|
|
1299
|
+
const destDir = path10.join(agentsDir ?? DEFAULT_AGENTS_DIR, agentId);
|
|
1300
|
+
fs11.mkdirSync(destDir, { recursive: true });
|
|
1345
1301
|
await progress?.onStep("Downloading...");
|
|
1346
|
-
|
|
1302
|
+
log10.info({ agentId, url: archiveUrl }, "Downloading agent binary");
|
|
1347
1303
|
const response = await fetch(archiveUrl);
|
|
1348
1304
|
if (!response.ok) {
|
|
1349
1305
|
throw new Error(`Download failed: ${response.status} ${response.statusText}`);
|
|
@@ -1382,70 +1338,70 @@ async function readResponseWithProgress(response, contentLength, progress) {
|
|
|
1382
1338
|
return Buffer.concat(chunks);
|
|
1383
1339
|
}
|
|
1384
1340
|
function validateExtractedPaths(destDir) {
|
|
1385
|
-
const realDest =
|
|
1386
|
-
const entries =
|
|
1341
|
+
const realDest = fs11.realpathSync(destDir);
|
|
1342
|
+
const entries = fs11.readdirSync(destDir, { recursive: true, withFileTypes: true });
|
|
1387
1343
|
for (const entry of entries) {
|
|
1388
1344
|
const dirent = entry;
|
|
1389
1345
|
const parentPath = dirent.parentPath ?? dirent.path ?? destDir;
|
|
1390
|
-
const fullPath =
|
|
1346
|
+
const fullPath = path10.join(parentPath, entry.name);
|
|
1391
1347
|
let realPath;
|
|
1392
1348
|
try {
|
|
1393
|
-
realPath =
|
|
1349
|
+
realPath = fs11.realpathSync(fullPath);
|
|
1394
1350
|
} catch {
|
|
1395
|
-
const linkTarget =
|
|
1396
|
-
realPath =
|
|
1351
|
+
const linkTarget = fs11.readlinkSync(fullPath);
|
|
1352
|
+
realPath = path10.resolve(path10.dirname(fullPath), linkTarget);
|
|
1397
1353
|
}
|
|
1398
|
-
if (!realPath.startsWith(realDest +
|
|
1399
|
-
|
|
1354
|
+
if (!realPath.startsWith(realDest + path10.sep) && realPath !== realDest) {
|
|
1355
|
+
fs11.rmSync(destDir, { recursive: true, force: true });
|
|
1400
1356
|
throw new Error(`Archive contains unsafe path: ${entry.name}`);
|
|
1401
1357
|
}
|
|
1402
1358
|
}
|
|
1403
1359
|
}
|
|
1404
1360
|
async function extractTarGz(buffer, destDir) {
|
|
1405
1361
|
const { execFileSync: execFileSync8 } = await import("child_process");
|
|
1406
|
-
const tmpFile =
|
|
1407
|
-
|
|
1362
|
+
const tmpFile = path10.join(destDir, "_archive.tar.gz");
|
|
1363
|
+
fs11.writeFileSync(tmpFile, buffer);
|
|
1408
1364
|
try {
|
|
1409
1365
|
const listing = execFileSync8("tar", ["tf", tmpFile], { stdio: "pipe" }).toString().trim().split("\n").filter(Boolean);
|
|
1410
1366
|
validateArchiveContents(listing, destDir);
|
|
1411
1367
|
execFileSync8("tar", ["xzf", tmpFile, "-C", destDir], { stdio: "pipe" });
|
|
1412
1368
|
} finally {
|
|
1413
|
-
|
|
1369
|
+
fs11.unlinkSync(tmpFile);
|
|
1414
1370
|
}
|
|
1415
1371
|
validateExtractedPaths(destDir);
|
|
1416
1372
|
}
|
|
1417
1373
|
async function extractZip(buffer, destDir) {
|
|
1418
1374
|
const { execFileSync: execFileSync8 } = await import("child_process");
|
|
1419
|
-
const tmpFile =
|
|
1420
|
-
|
|
1375
|
+
const tmpFile = path10.join(destDir, "_archive.zip");
|
|
1376
|
+
fs11.writeFileSync(tmpFile, buffer);
|
|
1421
1377
|
try {
|
|
1422
1378
|
const listing = execFileSync8("unzip", ["-l", tmpFile], { stdio: "pipe" }).toString().trim().split("\n").filter(Boolean);
|
|
1423
1379
|
const entries = listing.slice(3, -2).map((line) => line.trim().split(/\s+/).slice(3).join(" ")).filter(Boolean);
|
|
1424
1380
|
validateArchiveContents(entries, destDir);
|
|
1425
1381
|
execFileSync8("unzip", ["-o", tmpFile, "-d", destDir], { stdio: "pipe" });
|
|
1426
1382
|
} finally {
|
|
1427
|
-
|
|
1383
|
+
fs11.unlinkSync(tmpFile);
|
|
1428
1384
|
}
|
|
1429
1385
|
validateExtractedPaths(destDir);
|
|
1430
1386
|
}
|
|
1431
1387
|
async function uninstallAgent(agentKey, store, agentsDir) {
|
|
1432
1388
|
const agent = store.getAgent(agentKey);
|
|
1433
1389
|
if (!agent) return;
|
|
1434
|
-
if (agent.binaryPath &&
|
|
1390
|
+
if (agent.binaryPath && fs11.existsSync(agent.binaryPath)) {
|
|
1435
1391
|
validateUninstallPath(agent.binaryPath, agentsDir ?? DEFAULT_AGENTS_DIR);
|
|
1436
|
-
|
|
1437
|
-
|
|
1392
|
+
fs11.rmSync(agent.binaryPath, { recursive: true, force: true });
|
|
1393
|
+
log10.info({ agentKey, binaryPath: agent.binaryPath }, "Deleted agent binary");
|
|
1438
1394
|
}
|
|
1439
1395
|
store.removeAgent(agentKey);
|
|
1440
1396
|
}
|
|
1441
|
-
var
|
|
1397
|
+
var log10, DEFAULT_AGENTS_DIR, MAX_DOWNLOAD_SIZE, validateTarContents, ARCH_MAP, PLATFORM_MAP;
|
|
1442
1398
|
var init_agent_installer = __esm({
|
|
1443
1399
|
"src/core/agents/agent-installer.ts"() {
|
|
1444
1400
|
"use strict";
|
|
1445
1401
|
init_log();
|
|
1446
1402
|
init_agent_dependencies();
|
|
1447
|
-
|
|
1448
|
-
DEFAULT_AGENTS_DIR =
|
|
1403
|
+
log10 = createChildLogger({ module: "agent-installer" });
|
|
1404
|
+
DEFAULT_AGENTS_DIR = path10.join(os4.homedir(), ".openacp", "agents");
|
|
1449
1405
|
MAX_DOWNLOAD_SIZE = 500 * 1024 * 1024;
|
|
1450
1406
|
validateTarContents = validateArchiveContents;
|
|
1451
1407
|
ARCH_MAP = {
|
|
@@ -1461,7 +1417,7 @@ var init_agent_installer = __esm({
|
|
|
1461
1417
|
});
|
|
1462
1418
|
|
|
1463
1419
|
// src/core/doctor/checks/config.ts
|
|
1464
|
-
import * as
|
|
1420
|
+
import * as fs15 from "fs";
|
|
1465
1421
|
var configCheck;
|
|
1466
1422
|
var init_config2 = __esm({
|
|
1467
1423
|
"src/core/doctor/checks/config.ts"() {
|
|
@@ -1473,14 +1429,14 @@ var init_config2 = __esm({
|
|
|
1473
1429
|
order: 1,
|
|
1474
1430
|
async run(ctx) {
|
|
1475
1431
|
const results = [];
|
|
1476
|
-
if (!
|
|
1432
|
+
if (!fs15.existsSync(ctx.configPath)) {
|
|
1477
1433
|
results.push({ status: "fail", message: "Config file not found" });
|
|
1478
1434
|
return results;
|
|
1479
1435
|
}
|
|
1480
1436
|
results.push({ status: "pass", message: "Config file exists" });
|
|
1481
1437
|
let raw;
|
|
1482
1438
|
try {
|
|
1483
|
-
raw = JSON.parse(
|
|
1439
|
+
raw = JSON.parse(fs15.readFileSync(ctx.configPath, "utf-8"));
|
|
1484
1440
|
} catch (err) {
|
|
1485
1441
|
results.push({
|
|
1486
1442
|
status: "fail",
|
|
@@ -1499,7 +1455,7 @@ var init_config2 = __esm({
|
|
|
1499
1455
|
fixRisk: "safe",
|
|
1500
1456
|
fix: async () => {
|
|
1501
1457
|
applyMigrations(raw);
|
|
1502
|
-
|
|
1458
|
+
fs15.writeFileSync(ctx.configPath, JSON.stringify(raw, null, 2));
|
|
1503
1459
|
return { success: true, message: "applied migrations" };
|
|
1504
1460
|
}
|
|
1505
1461
|
});
|
|
@@ -1523,8 +1479,8 @@ var init_config2 = __esm({
|
|
|
1523
1479
|
|
|
1524
1480
|
// src/core/doctor/checks/agents.ts
|
|
1525
1481
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
1526
|
-
import * as
|
|
1527
|
-
import * as
|
|
1482
|
+
import * as fs16 from "fs";
|
|
1483
|
+
import * as path15 from "path";
|
|
1528
1484
|
function commandExists2(cmd) {
|
|
1529
1485
|
try {
|
|
1530
1486
|
execFileSync3("which", [cmd], { stdio: "pipe" });
|
|
@@ -1533,9 +1489,9 @@ function commandExists2(cmd) {
|
|
|
1533
1489
|
}
|
|
1534
1490
|
let dir = process.cwd();
|
|
1535
1491
|
while (true) {
|
|
1536
|
-
const binPath =
|
|
1537
|
-
if (
|
|
1538
|
-
const parent =
|
|
1492
|
+
const binPath = path15.join(dir, "node_modules", ".bin", cmd);
|
|
1493
|
+
if (fs16.existsSync(binPath)) return true;
|
|
1494
|
+
const parent = path15.dirname(dir);
|
|
1539
1495
|
if (parent === dir) break;
|
|
1540
1496
|
dir = parent;
|
|
1541
1497
|
}
|
|
@@ -1557,9 +1513,9 @@ var init_agents = __esm({
|
|
|
1557
1513
|
const defaultAgent = ctx.config.defaultAgent;
|
|
1558
1514
|
let agents = {};
|
|
1559
1515
|
try {
|
|
1560
|
-
const agentsPath =
|
|
1561
|
-
if (
|
|
1562
|
-
const data = JSON.parse(
|
|
1516
|
+
const agentsPath = path15.join(ctx.dataDir, "agents.json");
|
|
1517
|
+
if (fs16.existsSync(agentsPath)) {
|
|
1518
|
+
const data = JSON.parse(fs16.readFileSync(agentsPath, "utf-8"));
|
|
1563
1519
|
agents = data.installed ?? {};
|
|
1564
1520
|
}
|
|
1565
1521
|
} catch {
|
|
@@ -1597,8 +1553,8 @@ var settings_manager_exports = {};
|
|
|
1597
1553
|
__export(settings_manager_exports, {
|
|
1598
1554
|
SettingsManager: () => SettingsManager
|
|
1599
1555
|
});
|
|
1600
|
-
import
|
|
1601
|
-
import
|
|
1556
|
+
import fs17 from "fs";
|
|
1557
|
+
import path16 from "path";
|
|
1602
1558
|
var SettingsManager, SettingsAPIImpl;
|
|
1603
1559
|
var init_settings_manager = __esm({
|
|
1604
1560
|
"src/core/plugin/settings-manager.ts"() {
|
|
@@ -1617,7 +1573,7 @@ var init_settings_manager = __esm({
|
|
|
1617
1573
|
async loadSettings(pluginName) {
|
|
1618
1574
|
const settingsPath = this.getSettingsPath(pluginName);
|
|
1619
1575
|
try {
|
|
1620
|
-
const content =
|
|
1576
|
+
const content = fs17.readFileSync(settingsPath, "utf-8");
|
|
1621
1577
|
return JSON.parse(content);
|
|
1622
1578
|
} catch {
|
|
1623
1579
|
return {};
|
|
@@ -1635,7 +1591,7 @@ var init_settings_manager = __esm({
|
|
|
1635
1591
|
};
|
|
1636
1592
|
}
|
|
1637
1593
|
getSettingsPath(pluginName) {
|
|
1638
|
-
return
|
|
1594
|
+
return path16.join(this.basePath, pluginName, "settings.json");
|
|
1639
1595
|
}
|
|
1640
1596
|
async getPluginSettings(pluginName) {
|
|
1641
1597
|
return this.loadSettings(pluginName);
|
|
@@ -1654,7 +1610,7 @@ var init_settings_manager = __esm({
|
|
|
1654
1610
|
readFile() {
|
|
1655
1611
|
if (this.cache !== null) return this.cache;
|
|
1656
1612
|
try {
|
|
1657
|
-
const content =
|
|
1613
|
+
const content = fs17.readFileSync(this.settingsPath, "utf-8");
|
|
1658
1614
|
this.cache = JSON.parse(content);
|
|
1659
1615
|
return this.cache;
|
|
1660
1616
|
} catch {
|
|
@@ -1663,9 +1619,9 @@ var init_settings_manager = __esm({
|
|
|
1663
1619
|
}
|
|
1664
1620
|
}
|
|
1665
1621
|
writeFile(data) {
|
|
1666
|
-
const dir =
|
|
1667
|
-
|
|
1668
|
-
|
|
1622
|
+
const dir = path16.dirname(this.settingsPath);
|
|
1623
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
1624
|
+
fs17.writeFileSync(this.settingsPath, JSON.stringify(data, null, 2));
|
|
1669
1625
|
this.cache = data;
|
|
1670
1626
|
}
|
|
1671
1627
|
async get(key) {
|
|
@@ -1700,7 +1656,7 @@ var init_settings_manager = __esm({
|
|
|
1700
1656
|
});
|
|
1701
1657
|
|
|
1702
1658
|
// src/core/doctor/checks/telegram.ts
|
|
1703
|
-
import * as
|
|
1659
|
+
import * as path17 from "path";
|
|
1704
1660
|
var BOT_TOKEN_REGEX, telegramCheck;
|
|
1705
1661
|
var init_telegram = __esm({
|
|
1706
1662
|
"src/core/doctor/checks/telegram.ts"() {
|
|
@@ -1716,7 +1672,7 @@ var init_telegram = __esm({
|
|
|
1716
1672
|
return results;
|
|
1717
1673
|
}
|
|
1718
1674
|
const { SettingsManager: SettingsManager2 } = await Promise.resolve().then(() => (init_settings_manager(), settings_manager_exports));
|
|
1719
|
-
const sm = new SettingsManager2(
|
|
1675
|
+
const sm = new SettingsManager2(path17.join(ctx.pluginsDir, "data"));
|
|
1720
1676
|
const ps = await sm.loadSettings("@openacp/telegram");
|
|
1721
1677
|
const botToken = ps.botToken;
|
|
1722
1678
|
const chatId = ps.chatId;
|
|
@@ -1799,7 +1755,7 @@ var init_telegram = __esm({
|
|
|
1799
1755
|
});
|
|
1800
1756
|
|
|
1801
1757
|
// src/core/doctor/checks/storage.ts
|
|
1802
|
-
import * as
|
|
1758
|
+
import * as fs18 from "fs";
|
|
1803
1759
|
var storageCheck;
|
|
1804
1760
|
var init_storage = __esm({
|
|
1805
1761
|
"src/core/doctor/checks/storage.ts"() {
|
|
@@ -1809,28 +1765,28 @@ var init_storage = __esm({
|
|
|
1809
1765
|
order: 4,
|
|
1810
1766
|
async run(ctx) {
|
|
1811
1767
|
const results = [];
|
|
1812
|
-
if (!
|
|
1768
|
+
if (!fs18.existsSync(ctx.dataDir)) {
|
|
1813
1769
|
results.push({
|
|
1814
1770
|
status: "fail",
|
|
1815
1771
|
message: "Data directory ~/.openacp does not exist",
|
|
1816
1772
|
fixable: true,
|
|
1817
1773
|
fixRisk: "safe",
|
|
1818
1774
|
fix: async () => {
|
|
1819
|
-
|
|
1775
|
+
fs18.mkdirSync(ctx.dataDir, { recursive: true });
|
|
1820
1776
|
return { success: true, message: "created directory" };
|
|
1821
1777
|
}
|
|
1822
1778
|
});
|
|
1823
1779
|
} else {
|
|
1824
1780
|
try {
|
|
1825
|
-
|
|
1781
|
+
fs18.accessSync(ctx.dataDir, fs18.constants.W_OK);
|
|
1826
1782
|
results.push({ status: "pass", message: "Data directory exists and writable" });
|
|
1827
1783
|
} catch {
|
|
1828
1784
|
results.push({ status: "fail", message: "Data directory not writable" });
|
|
1829
1785
|
}
|
|
1830
1786
|
}
|
|
1831
|
-
if (
|
|
1787
|
+
if (fs18.existsSync(ctx.sessionsPath)) {
|
|
1832
1788
|
try {
|
|
1833
|
-
const content =
|
|
1789
|
+
const content = fs18.readFileSync(ctx.sessionsPath, "utf-8");
|
|
1834
1790
|
const data = JSON.parse(content);
|
|
1835
1791
|
if (typeof data === "object" && data !== null && "sessions" in data) {
|
|
1836
1792
|
results.push({ status: "pass", message: "Sessions file valid" });
|
|
@@ -1841,7 +1797,7 @@ var init_storage = __esm({
|
|
|
1841
1797
|
fixable: true,
|
|
1842
1798
|
fixRisk: "risky",
|
|
1843
1799
|
fix: async () => {
|
|
1844
|
-
|
|
1800
|
+
fs18.writeFileSync(ctx.sessionsPath, JSON.stringify({ version: 1, sessions: {} }, null, 2));
|
|
1845
1801
|
return { success: true, message: "reset sessions file" };
|
|
1846
1802
|
}
|
|
1847
1803
|
});
|
|
@@ -1853,7 +1809,7 @@ var init_storage = __esm({
|
|
|
1853
1809
|
fixable: true,
|
|
1854
1810
|
fixRisk: "risky",
|
|
1855
1811
|
fix: async () => {
|
|
1856
|
-
|
|
1812
|
+
fs18.writeFileSync(ctx.sessionsPath, JSON.stringify({ version: 1, sessions: {} }, null, 2));
|
|
1857
1813
|
return { success: true, message: "reset sessions file" };
|
|
1858
1814
|
}
|
|
1859
1815
|
});
|
|
@@ -1861,20 +1817,20 @@ var init_storage = __esm({
|
|
|
1861
1817
|
} else {
|
|
1862
1818
|
results.push({ status: "pass", message: "Sessions file not present yet (created on first session)" });
|
|
1863
1819
|
}
|
|
1864
|
-
if (!
|
|
1820
|
+
if (!fs18.existsSync(ctx.logsDir)) {
|
|
1865
1821
|
results.push({
|
|
1866
1822
|
status: "warn",
|
|
1867
1823
|
message: "Log directory does not exist",
|
|
1868
1824
|
fixable: true,
|
|
1869
1825
|
fixRisk: "safe",
|
|
1870
1826
|
fix: async () => {
|
|
1871
|
-
|
|
1827
|
+
fs18.mkdirSync(ctx.logsDir, { recursive: true });
|
|
1872
1828
|
return { success: true, message: "created log directory" };
|
|
1873
1829
|
}
|
|
1874
1830
|
});
|
|
1875
1831
|
} else {
|
|
1876
1832
|
try {
|
|
1877
|
-
|
|
1833
|
+
fs18.accessSync(ctx.logsDir, fs18.constants.W_OK);
|
|
1878
1834
|
results.push({ status: "pass", message: "Log directory exists and writable" });
|
|
1879
1835
|
} catch {
|
|
1880
1836
|
results.push({ status: "fail", message: "Log directory not writable" });
|
|
@@ -1887,39 +1843,35 @@ var init_storage = __esm({
|
|
|
1887
1843
|
});
|
|
1888
1844
|
|
|
1889
1845
|
// src/core/doctor/checks/workspace.ts
|
|
1890
|
-
import * as
|
|
1846
|
+
import * as fs19 from "fs";
|
|
1847
|
+
import * as path18 from "path";
|
|
1891
1848
|
var workspaceCheck;
|
|
1892
1849
|
var init_workspace = __esm({
|
|
1893
1850
|
"src/core/doctor/checks/workspace.ts"() {
|
|
1894
1851
|
"use strict";
|
|
1895
|
-
init_config();
|
|
1896
1852
|
workspaceCheck = {
|
|
1897
1853
|
name: "Workspace",
|
|
1898
1854
|
order: 5,
|
|
1899
1855
|
async run(ctx) {
|
|
1900
1856
|
const results = [];
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
return results;
|
|
1904
|
-
}
|
|
1905
|
-
const baseDir = expandHome3(ctx.config.workspace.baseDir);
|
|
1906
|
-
if (!fs20.existsSync(baseDir)) {
|
|
1857
|
+
const workspace = path18.dirname(ctx.dataDir);
|
|
1858
|
+
if (!fs19.existsSync(workspace)) {
|
|
1907
1859
|
results.push({
|
|
1908
1860
|
status: "warn",
|
|
1909
|
-
message: `Workspace directory does not exist: ${
|
|
1861
|
+
message: `Workspace directory does not exist: ${workspace}`,
|
|
1910
1862
|
fixable: true,
|
|
1911
1863
|
fixRisk: "safe",
|
|
1912
1864
|
fix: async () => {
|
|
1913
|
-
|
|
1865
|
+
fs19.mkdirSync(workspace, { recursive: true });
|
|
1914
1866
|
return { success: true, message: "created directory" };
|
|
1915
1867
|
}
|
|
1916
1868
|
});
|
|
1917
1869
|
} else {
|
|
1918
1870
|
try {
|
|
1919
|
-
|
|
1920
|
-
results.push({ status: "pass", message: `Workspace directory exists: ${
|
|
1871
|
+
fs19.accessSync(workspace, fs19.constants.W_OK);
|
|
1872
|
+
results.push({ status: "pass", message: `Workspace directory exists: ${workspace}` });
|
|
1921
1873
|
} catch {
|
|
1922
|
-
results.push({ status: "fail", message: `Workspace directory not writable: ${
|
|
1874
|
+
results.push({ status: "fail", message: `Workspace directory not writable: ${workspace}` });
|
|
1923
1875
|
}
|
|
1924
1876
|
}
|
|
1925
1877
|
return results;
|
|
@@ -1929,8 +1881,8 @@ var init_workspace = __esm({
|
|
|
1929
1881
|
});
|
|
1930
1882
|
|
|
1931
1883
|
// src/core/doctor/checks/plugins.ts
|
|
1932
|
-
import * as
|
|
1933
|
-
import * as
|
|
1884
|
+
import * as fs20 from "fs";
|
|
1885
|
+
import * as path19 from "path";
|
|
1934
1886
|
var pluginsCheck;
|
|
1935
1887
|
var init_plugins = __esm({
|
|
1936
1888
|
"src/core/doctor/checks/plugins.ts"() {
|
|
@@ -1940,16 +1892,16 @@ var init_plugins = __esm({
|
|
|
1940
1892
|
order: 6,
|
|
1941
1893
|
async run(ctx) {
|
|
1942
1894
|
const results = [];
|
|
1943
|
-
if (!
|
|
1895
|
+
if (!fs20.existsSync(ctx.pluginsDir)) {
|
|
1944
1896
|
results.push({
|
|
1945
1897
|
status: "warn",
|
|
1946
1898
|
message: "Plugins directory does not exist",
|
|
1947
1899
|
fixable: true,
|
|
1948
1900
|
fixRisk: "safe",
|
|
1949
1901
|
fix: async () => {
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1902
|
+
fs20.mkdirSync(ctx.pluginsDir, { recursive: true });
|
|
1903
|
+
fs20.writeFileSync(
|
|
1904
|
+
path19.join(ctx.pluginsDir, "package.json"),
|
|
1953
1905
|
JSON.stringify({ name: "openacp-plugins", private: true, dependencies: {} }, null, 2)
|
|
1954
1906
|
);
|
|
1955
1907
|
return { success: true, message: "initialized plugins directory" };
|
|
@@ -1958,15 +1910,15 @@ var init_plugins = __esm({
|
|
|
1958
1910
|
return results;
|
|
1959
1911
|
}
|
|
1960
1912
|
results.push({ status: "pass", message: "Plugins directory exists" });
|
|
1961
|
-
const pkgPath =
|
|
1962
|
-
if (!
|
|
1913
|
+
const pkgPath = path19.join(ctx.pluginsDir, "package.json");
|
|
1914
|
+
if (!fs20.existsSync(pkgPath)) {
|
|
1963
1915
|
results.push({
|
|
1964
1916
|
status: "warn",
|
|
1965
1917
|
message: "Plugins package.json missing",
|
|
1966
1918
|
fixable: true,
|
|
1967
1919
|
fixRisk: "safe",
|
|
1968
1920
|
fix: async () => {
|
|
1969
|
-
|
|
1921
|
+
fs20.writeFileSync(
|
|
1970
1922
|
pkgPath,
|
|
1971
1923
|
JSON.stringify({ name: "openacp-plugins", private: true, dependencies: {} }, null, 2)
|
|
1972
1924
|
);
|
|
@@ -1976,7 +1928,7 @@ var init_plugins = __esm({
|
|
|
1976
1928
|
return results;
|
|
1977
1929
|
}
|
|
1978
1930
|
try {
|
|
1979
|
-
const pkg = JSON.parse(
|
|
1931
|
+
const pkg = JSON.parse(fs20.readFileSync(pkgPath, "utf-8"));
|
|
1980
1932
|
const deps = pkg.dependencies || {};
|
|
1981
1933
|
const count = Object.keys(deps).length;
|
|
1982
1934
|
results.push({ status: "pass", message: `Plugins package.json valid (${count} plugins)` });
|
|
@@ -1987,7 +1939,7 @@ var init_plugins = __esm({
|
|
|
1987
1939
|
fixable: true,
|
|
1988
1940
|
fixRisk: "risky",
|
|
1989
1941
|
fix: async () => {
|
|
1990
|
-
|
|
1942
|
+
fs20.writeFileSync(
|
|
1991
1943
|
pkgPath,
|
|
1992
1944
|
JSON.stringify({ name: "openacp-plugins", private: true, dependencies: {} }, null, 2)
|
|
1993
1945
|
);
|
|
@@ -2002,7 +1954,7 @@ var init_plugins = __esm({
|
|
|
2002
1954
|
});
|
|
2003
1955
|
|
|
2004
1956
|
// src/core/doctor/checks/daemon.ts
|
|
2005
|
-
import * as
|
|
1957
|
+
import * as fs21 from "fs";
|
|
2006
1958
|
import * as net from "net";
|
|
2007
1959
|
function isProcessAlive(pid) {
|
|
2008
1960
|
try {
|
|
@@ -2013,12 +1965,12 @@ function isProcessAlive(pid) {
|
|
|
2013
1965
|
}
|
|
2014
1966
|
}
|
|
2015
1967
|
function checkPortInUse(port) {
|
|
2016
|
-
return new Promise((
|
|
1968
|
+
return new Promise((resolve7) => {
|
|
2017
1969
|
const server = net.createServer();
|
|
2018
|
-
server.once("error", () =>
|
|
1970
|
+
server.once("error", () => resolve7(true));
|
|
2019
1971
|
server.once("listening", () => {
|
|
2020
1972
|
server.close();
|
|
2021
|
-
|
|
1973
|
+
resolve7(false);
|
|
2022
1974
|
});
|
|
2023
1975
|
server.listen(port, "127.0.0.1");
|
|
2024
1976
|
});
|
|
@@ -2032,8 +1984,8 @@ var init_daemon = __esm({
|
|
|
2032
1984
|
order: 7,
|
|
2033
1985
|
async run(ctx) {
|
|
2034
1986
|
const results = [];
|
|
2035
|
-
if (
|
|
2036
|
-
const content =
|
|
1987
|
+
if (fs21.existsSync(ctx.pidPath)) {
|
|
1988
|
+
const content = fs21.readFileSync(ctx.pidPath, "utf-8").trim();
|
|
2037
1989
|
const pid = parseInt(content, 10);
|
|
2038
1990
|
if (isNaN(pid)) {
|
|
2039
1991
|
results.push({
|
|
@@ -2042,7 +1994,7 @@ var init_daemon = __esm({
|
|
|
2042
1994
|
fixable: true,
|
|
2043
1995
|
fixRisk: "safe",
|
|
2044
1996
|
fix: async () => {
|
|
2045
|
-
|
|
1997
|
+
fs21.unlinkSync(ctx.pidPath);
|
|
2046
1998
|
return { success: true, message: "removed invalid PID file" };
|
|
2047
1999
|
}
|
|
2048
2000
|
});
|
|
@@ -2053,7 +2005,7 @@ var init_daemon = __esm({
|
|
|
2053
2005
|
fixable: true,
|
|
2054
2006
|
fixRisk: "safe",
|
|
2055
2007
|
fix: async () => {
|
|
2056
|
-
|
|
2008
|
+
fs21.unlinkSync(ctx.pidPath);
|
|
2057
2009
|
return { success: true, message: "removed stale PID file" };
|
|
2058
2010
|
}
|
|
2059
2011
|
});
|
|
@@ -2061,8 +2013,8 @@ var init_daemon = __esm({
|
|
|
2061
2013
|
results.push({ status: "pass", message: `Daemon running (PID ${pid})` });
|
|
2062
2014
|
}
|
|
2063
2015
|
}
|
|
2064
|
-
if (
|
|
2065
|
-
const content =
|
|
2016
|
+
if (fs21.existsSync(ctx.portFilePath)) {
|
|
2017
|
+
const content = fs21.readFileSync(ctx.portFilePath, "utf-8").trim();
|
|
2066
2018
|
const port = parseInt(content, 10);
|
|
2067
2019
|
if (isNaN(port)) {
|
|
2068
2020
|
results.push({
|
|
@@ -2071,7 +2023,7 @@ var init_daemon = __esm({
|
|
|
2071
2023
|
fixable: true,
|
|
2072
2024
|
fixRisk: "safe",
|
|
2073
2025
|
fix: async () => {
|
|
2074
|
-
|
|
2026
|
+
fs21.unlinkSync(ctx.portFilePath);
|
|
2075
2027
|
return { success: true, message: "removed invalid port file" };
|
|
2076
2028
|
}
|
|
2077
2029
|
});
|
|
@@ -2083,8 +2035,8 @@ var init_daemon = __esm({
|
|
|
2083
2035
|
const apiPort = 21420;
|
|
2084
2036
|
const inUse = await checkPortInUse(apiPort);
|
|
2085
2037
|
if (inUse) {
|
|
2086
|
-
if (
|
|
2087
|
-
const pid = parseInt(
|
|
2038
|
+
if (fs21.existsSync(ctx.pidPath)) {
|
|
2039
|
+
const pid = parseInt(fs21.readFileSync(ctx.pidPath, "utf-8").trim(), 10);
|
|
2088
2040
|
if (!isNaN(pid) && isProcessAlive(pid)) {
|
|
2089
2041
|
results.push({ status: "pass", message: `API port ${apiPort} in use by OpenACP daemon` });
|
|
2090
2042
|
} else {
|
|
@@ -2104,17 +2056,17 @@ var init_daemon = __esm({
|
|
|
2104
2056
|
});
|
|
2105
2057
|
|
|
2106
2058
|
// src/core/utils/install-binary.ts
|
|
2107
|
-
import
|
|
2108
|
-
import
|
|
2059
|
+
import fs22 from "fs";
|
|
2060
|
+
import path20 from "path";
|
|
2109
2061
|
import https from "https";
|
|
2110
|
-
import
|
|
2062
|
+
import os5 from "os";
|
|
2111
2063
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
2112
2064
|
function downloadFile(url, dest, maxRedirects = 10) {
|
|
2113
|
-
return new Promise((
|
|
2114
|
-
const file =
|
|
2065
|
+
return new Promise((resolve7, reject) => {
|
|
2066
|
+
const file = fs22.createWriteStream(dest);
|
|
2115
2067
|
const cleanup = () => {
|
|
2116
2068
|
try {
|
|
2117
|
-
if (
|
|
2069
|
+
if (fs22.existsSync(dest)) fs22.unlinkSync(dest);
|
|
2118
2070
|
} catch {
|
|
2119
2071
|
}
|
|
2120
2072
|
};
|
|
@@ -2129,7 +2081,7 @@ function downloadFile(url, dest, maxRedirects = 10) {
|
|
|
2129
2081
|
}
|
|
2130
2082
|
file.close(() => {
|
|
2131
2083
|
cleanup();
|
|
2132
|
-
downloadFile(response.headers.location, dest, maxRedirects - 1).then(
|
|
2084
|
+
downloadFile(response.headers.location, dest, maxRedirects - 1).then(resolve7).catch(reject);
|
|
2133
2085
|
});
|
|
2134
2086
|
return;
|
|
2135
2087
|
}
|
|
@@ -2153,7 +2105,7 @@ function downloadFile(url, dest, maxRedirects = 10) {
|
|
|
2153
2105
|
}
|
|
2154
2106
|
});
|
|
2155
2107
|
response.pipe(file);
|
|
2156
|
-
file.on("finish", () => file.close(() =>
|
|
2108
|
+
file.on("finish", () => file.close(() => resolve7(dest)));
|
|
2157
2109
|
file.on("error", (err) => {
|
|
2158
2110
|
file.close(() => {
|
|
2159
2111
|
cleanup();
|
|
@@ -2169,8 +2121,8 @@ function downloadFile(url, dest, maxRedirects = 10) {
|
|
|
2169
2121
|
});
|
|
2170
2122
|
}
|
|
2171
2123
|
function getDownloadUrl(spec) {
|
|
2172
|
-
const platform2 =
|
|
2173
|
-
const arch =
|
|
2124
|
+
const platform2 = os5.platform();
|
|
2125
|
+
const arch = os5.arch();
|
|
2174
2126
|
const mapping = spec.platforms[platform2];
|
|
2175
2127
|
if (!mapping) throw new Error(`${spec.name}: unsupported platform ${platform2}`);
|
|
2176
2128
|
const binary = mapping[arch];
|
|
@@ -2180,36 +2132,36 @@ function getDownloadUrl(spec) {
|
|
|
2180
2132
|
async function ensureBinary(spec, binDir) {
|
|
2181
2133
|
const resolvedBinDir = binDir ?? DEFAULT_BIN_DIR;
|
|
2182
2134
|
const binName = IS_WINDOWS ? `${spec.name}.exe` : spec.name;
|
|
2183
|
-
const binPath =
|
|
2135
|
+
const binPath = path20.join(resolvedBinDir, binName);
|
|
2184
2136
|
if (commandExists(spec.name)) {
|
|
2185
2137
|
log17.debug({ name: spec.name }, "Found in PATH");
|
|
2186
2138
|
return spec.name;
|
|
2187
2139
|
}
|
|
2188
|
-
if (
|
|
2189
|
-
if (!IS_WINDOWS)
|
|
2140
|
+
if (fs22.existsSync(binPath)) {
|
|
2141
|
+
if (!IS_WINDOWS) fs22.chmodSync(binPath, "755");
|
|
2190
2142
|
log17.debug({ name: spec.name, path: binPath }, "Found in ~/.openacp/bin");
|
|
2191
2143
|
return binPath;
|
|
2192
2144
|
}
|
|
2193
2145
|
log17.info({ name: spec.name }, "Not found, downloading from GitHub...");
|
|
2194
|
-
|
|
2146
|
+
fs22.mkdirSync(resolvedBinDir, { recursive: true });
|
|
2195
2147
|
const url = getDownloadUrl(spec);
|
|
2196
2148
|
const isArchive = spec.isArchive?.(url) ?? false;
|
|
2197
|
-
const downloadDest = isArchive ?
|
|
2149
|
+
const downloadDest = isArchive ? path20.join(resolvedBinDir, `${spec.name}.tgz`) : binPath;
|
|
2198
2150
|
await downloadFile(url, downloadDest);
|
|
2199
2151
|
if (isArchive) {
|
|
2200
2152
|
const listing = execFileSync4("tar", ["-tf", downloadDest], { stdio: "pipe" }).toString().trim().split("\n").filter(Boolean);
|
|
2201
2153
|
validateTarContents(listing, resolvedBinDir);
|
|
2202
2154
|
execFileSync4("tar", ["-xzf", downloadDest, "-C", resolvedBinDir], { stdio: "pipe" });
|
|
2203
2155
|
try {
|
|
2204
|
-
|
|
2156
|
+
fs22.unlinkSync(downloadDest);
|
|
2205
2157
|
} catch {
|
|
2206
2158
|
}
|
|
2207
2159
|
}
|
|
2208
|
-
if (!
|
|
2160
|
+
if (!fs22.existsSync(binPath)) {
|
|
2209
2161
|
throw new Error(`${spec.name}: binary not found at ${binPath} after download/extraction. The archive structure may have changed.`);
|
|
2210
2162
|
}
|
|
2211
2163
|
if (!IS_WINDOWS) {
|
|
2212
|
-
|
|
2164
|
+
fs22.chmodSync(binPath, "755");
|
|
2213
2165
|
}
|
|
2214
2166
|
log17.info({ name: spec.name, path: binPath }, "Installed successfully");
|
|
2215
2167
|
return binPath;
|
|
@@ -2222,8 +2174,8 @@ var init_install_binary = __esm({
|
|
|
2222
2174
|
init_agent_dependencies();
|
|
2223
2175
|
init_agent_installer();
|
|
2224
2176
|
log17 = createChildLogger({ module: "binary-installer" });
|
|
2225
|
-
DEFAULT_BIN_DIR =
|
|
2226
|
-
IS_WINDOWS =
|
|
2177
|
+
DEFAULT_BIN_DIR = path20.join(os5.homedir(), ".openacp", "bin");
|
|
2178
|
+
IS_WINDOWS = os5.platform() === "win32";
|
|
2227
2179
|
}
|
|
2228
2180
|
});
|
|
2229
2181
|
|
|
@@ -2263,9 +2215,9 @@ var init_install_cloudflared = __esm({
|
|
|
2263
2215
|
});
|
|
2264
2216
|
|
|
2265
2217
|
// src/core/doctor/checks/tunnel.ts
|
|
2266
|
-
import * as
|
|
2267
|
-
import * as
|
|
2268
|
-
import * as
|
|
2218
|
+
import * as fs23 from "fs";
|
|
2219
|
+
import * as path21 from "path";
|
|
2220
|
+
import * as os6 from "os";
|
|
2269
2221
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
2270
2222
|
var tunnelCheck;
|
|
2271
2223
|
var init_tunnel = __esm({
|
|
@@ -2281,7 +2233,7 @@ var init_tunnel = __esm({
|
|
|
2281
2233
|
return results;
|
|
2282
2234
|
}
|
|
2283
2235
|
const { SettingsManager: SettingsManager2 } = await Promise.resolve().then(() => (init_settings_manager(), settings_manager_exports));
|
|
2284
|
-
const sm = new SettingsManager2(
|
|
2236
|
+
const sm = new SettingsManager2(path21.join(ctx.pluginsDir, "data"));
|
|
2285
2237
|
const tunnelSettings = await sm.loadSettings("@openacp/tunnel");
|
|
2286
2238
|
const tunnelEnabled = tunnelSettings.enabled ?? false;
|
|
2287
2239
|
const provider = tunnelSettings.provider ?? "cloudflare";
|
|
@@ -2292,10 +2244,10 @@ var init_tunnel = __esm({
|
|
|
2292
2244
|
}
|
|
2293
2245
|
results.push({ status: "pass", message: `Tunnel provider: ${provider}` });
|
|
2294
2246
|
if (provider === "cloudflare") {
|
|
2295
|
-
const binName =
|
|
2296
|
-
const binPath =
|
|
2247
|
+
const binName = os6.platform() === "win32" ? "cloudflared.exe" : "cloudflared";
|
|
2248
|
+
const binPath = path21.join(ctx.dataDir, "bin", binName);
|
|
2297
2249
|
let found = false;
|
|
2298
|
-
if (
|
|
2250
|
+
if (fs23.existsSync(binPath)) {
|
|
2299
2251
|
found = true;
|
|
2300
2252
|
} else {
|
|
2301
2253
|
try {
|
|
@@ -2336,14 +2288,13 @@ var init_tunnel = __esm({
|
|
|
2336
2288
|
});
|
|
2337
2289
|
|
|
2338
2290
|
// src/core/doctor/index.ts
|
|
2339
|
-
import * as
|
|
2340
|
-
import * as
|
|
2291
|
+
import * as fs24 from "fs";
|
|
2292
|
+
import * as path22 from "path";
|
|
2341
2293
|
var ALL_CHECKS, CHECK_TIMEOUT_MS, DoctorEngine;
|
|
2342
2294
|
var init_doctor = __esm({
|
|
2343
2295
|
"src/core/doctor/index.ts"() {
|
|
2344
2296
|
"use strict";
|
|
2345
2297
|
init_config();
|
|
2346
|
-
init_instance_context();
|
|
2347
2298
|
init_config2();
|
|
2348
2299
|
init_agents();
|
|
2349
2300
|
init_telegram();
|
|
@@ -2368,7 +2319,7 @@ var init_doctor = __esm({
|
|
|
2368
2319
|
dataDir;
|
|
2369
2320
|
constructor(options) {
|
|
2370
2321
|
this.dryRun = options?.dryRun ?? false;
|
|
2371
|
-
this.dataDir = options
|
|
2322
|
+
this.dataDir = options.dataDir;
|
|
2372
2323
|
}
|
|
2373
2324
|
async runAll() {
|
|
2374
2325
|
const ctx = await this.buildContext();
|
|
@@ -2419,27 +2370,27 @@ var init_doctor = __esm({
|
|
|
2419
2370
|
}
|
|
2420
2371
|
async buildContext() {
|
|
2421
2372
|
const dataDir = this.dataDir;
|
|
2422
|
-
const configPath = process.env.OPENACP_CONFIG_PATH ||
|
|
2373
|
+
const configPath = process.env.OPENACP_CONFIG_PATH || path22.join(dataDir, "config.json");
|
|
2423
2374
|
let config = null;
|
|
2424
2375
|
let rawConfig = null;
|
|
2425
2376
|
try {
|
|
2426
|
-
const content =
|
|
2377
|
+
const content = fs24.readFileSync(configPath, "utf-8");
|
|
2427
2378
|
rawConfig = JSON.parse(content);
|
|
2428
2379
|
const cm = new ConfigManager(configPath);
|
|
2429
2380
|
await cm.load();
|
|
2430
2381
|
config = cm.get();
|
|
2431
2382
|
} catch {
|
|
2432
2383
|
}
|
|
2433
|
-
const logsDir = config ?
|
|
2384
|
+
const logsDir = config ? expandHome2(config.logging.logDir) : path22.join(dataDir, "logs");
|
|
2434
2385
|
return {
|
|
2435
2386
|
config,
|
|
2436
2387
|
rawConfig,
|
|
2437
2388
|
configPath,
|
|
2438
2389
|
dataDir,
|
|
2439
|
-
sessionsPath:
|
|
2440
|
-
pidPath:
|
|
2441
|
-
portFilePath:
|
|
2442
|
-
pluginsDir:
|
|
2390
|
+
sessionsPath: path22.join(dataDir, "sessions.json"),
|
|
2391
|
+
pidPath: path22.join(dataDir, "openacp.pid"),
|
|
2392
|
+
portFilePath: path22.join(dataDir, "api.port"),
|
|
2393
|
+
pluginsDir: path22.join(dataDir, "plugins"),
|
|
2443
2394
|
logsDir
|
|
2444
2395
|
};
|
|
2445
2396
|
}
|
|
@@ -2447,6 +2398,123 @@ var init_doctor = __esm({
|
|
|
2447
2398
|
}
|
|
2448
2399
|
});
|
|
2449
2400
|
|
|
2401
|
+
// src/core/instance/instance-context.ts
|
|
2402
|
+
import path24 from "path";
|
|
2403
|
+
import fs26 from "fs";
|
|
2404
|
+
import os8 from "os";
|
|
2405
|
+
function getGlobalRoot() {
|
|
2406
|
+
return path24.join(os8.homedir(), ".openacp");
|
|
2407
|
+
}
|
|
2408
|
+
var init_instance_context = __esm({
|
|
2409
|
+
"src/core/instance/instance-context.ts"() {
|
|
2410
|
+
"use strict";
|
|
2411
|
+
}
|
|
2412
|
+
});
|
|
2413
|
+
|
|
2414
|
+
// src/core/instance/instance-registry.ts
|
|
2415
|
+
import fs27 from "fs";
|
|
2416
|
+
import path25 from "path";
|
|
2417
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2418
|
+
function readIdFromConfig(instanceRoot) {
|
|
2419
|
+
try {
|
|
2420
|
+
const raw = JSON.parse(fs27.readFileSync(path25.join(instanceRoot, "config.json"), "utf-8"));
|
|
2421
|
+
return typeof raw.id === "string" && raw.id ? raw.id : null;
|
|
2422
|
+
} catch {
|
|
2423
|
+
return null;
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
var InstanceRegistry;
|
|
2427
|
+
var init_instance_registry = __esm({
|
|
2428
|
+
"src/core/instance/instance-registry.ts"() {
|
|
2429
|
+
"use strict";
|
|
2430
|
+
InstanceRegistry = class {
|
|
2431
|
+
constructor(registryPath) {
|
|
2432
|
+
this.registryPath = registryPath;
|
|
2433
|
+
}
|
|
2434
|
+
data = { version: 1, instances: {} };
|
|
2435
|
+
load() {
|
|
2436
|
+
try {
|
|
2437
|
+
const raw = fs27.readFileSync(this.registryPath, "utf-8");
|
|
2438
|
+
const parsed = JSON.parse(raw);
|
|
2439
|
+
if (parsed.version === 1 && parsed.instances) {
|
|
2440
|
+
this.data = parsed;
|
|
2441
|
+
this.deduplicate();
|
|
2442
|
+
}
|
|
2443
|
+
} catch {
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
/** Remove duplicate entries that point to the same root, keeping the first one */
|
|
2447
|
+
deduplicate() {
|
|
2448
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2449
|
+
const toRemove = [];
|
|
2450
|
+
for (const [id, entry] of Object.entries(this.data.instances)) {
|
|
2451
|
+
if (seen.has(entry.root)) {
|
|
2452
|
+
toRemove.push(id);
|
|
2453
|
+
} else {
|
|
2454
|
+
seen.add(entry.root);
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
if (toRemove.length > 0) {
|
|
2458
|
+
for (const id of toRemove) delete this.data.instances[id];
|
|
2459
|
+
this.save();
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
save() {
|
|
2463
|
+
const dir = path25.dirname(this.registryPath);
|
|
2464
|
+
fs27.mkdirSync(dir, { recursive: true });
|
|
2465
|
+
fs27.writeFileSync(this.registryPath, JSON.stringify(this.data, null, 2));
|
|
2466
|
+
}
|
|
2467
|
+
register(id, root) {
|
|
2468
|
+
this.data.instances[id] = { id, root };
|
|
2469
|
+
}
|
|
2470
|
+
remove(id) {
|
|
2471
|
+
delete this.data.instances[id];
|
|
2472
|
+
}
|
|
2473
|
+
get(id) {
|
|
2474
|
+
return this.data.instances[id];
|
|
2475
|
+
}
|
|
2476
|
+
getByRoot(root) {
|
|
2477
|
+
return Object.values(this.data.instances).find((e) => e.root === root);
|
|
2478
|
+
}
|
|
2479
|
+
list() {
|
|
2480
|
+
return Object.values(this.data.instances);
|
|
2481
|
+
}
|
|
2482
|
+
uniqueId(baseId) {
|
|
2483
|
+
if (!this.data.instances[baseId]) return baseId;
|
|
2484
|
+
let n = 2;
|
|
2485
|
+
while (this.data.instances[`${baseId}-${n}`]) n++;
|
|
2486
|
+
return `${baseId}-${n}`;
|
|
2487
|
+
}
|
|
2488
|
+
/**
|
|
2489
|
+
* Resolve the authoritative id for an instance root.
|
|
2490
|
+
*
|
|
2491
|
+
* config.json is the source of truth. If the registry entry disagrees with
|
|
2492
|
+
* config.json, the registry is updated to match. If neither exists, a fresh
|
|
2493
|
+
* UUID is generated and registered (but NOT written to config.json — the
|
|
2494
|
+
* caller must call initInstanceFiles to persist it).
|
|
2495
|
+
*
|
|
2496
|
+
* Returns the resolved id and whether the registry was mutated (so callers
|
|
2497
|
+
* can decide whether to persist with save()).
|
|
2498
|
+
*/
|
|
2499
|
+
resolveId(instanceRoot) {
|
|
2500
|
+
const configId = readIdFromConfig(instanceRoot);
|
|
2501
|
+
const entry = this.getByRoot(instanceRoot);
|
|
2502
|
+
if (entry && configId && entry.id !== configId) {
|
|
2503
|
+
this.remove(entry.id);
|
|
2504
|
+
this.register(configId, instanceRoot);
|
|
2505
|
+
return { id: configId, registryUpdated: true };
|
|
2506
|
+
}
|
|
2507
|
+
const id = configId ?? entry?.id ?? randomUUID2();
|
|
2508
|
+
if (!this.getByRoot(instanceRoot)) {
|
|
2509
|
+
this.register(id, instanceRoot);
|
|
2510
|
+
return { id, registryUpdated: true };
|
|
2511
|
+
}
|
|
2512
|
+
return { id, registryUpdated: false };
|
|
2513
|
+
}
|
|
2514
|
+
};
|
|
2515
|
+
}
|
|
2516
|
+
});
|
|
2517
|
+
|
|
2450
2518
|
// src/core/plugin/plugin-installer.ts
|
|
2451
2519
|
var plugin_installer_exports = {};
|
|
2452
2520
|
__export(plugin_installer_exports, {
|
|
@@ -2455,16 +2523,15 @@ __export(plugin_installer_exports, {
|
|
|
2455
2523
|
});
|
|
2456
2524
|
import { execFile } from "child_process";
|
|
2457
2525
|
import { promisify } from "util";
|
|
2458
|
-
import * as
|
|
2459
|
-
import * as
|
|
2460
|
-
import * as path25 from "path";
|
|
2526
|
+
import * as fs29 from "fs/promises";
|
|
2527
|
+
import * as path27 from "path";
|
|
2461
2528
|
import { pathToFileURL } from "url";
|
|
2462
2529
|
async function importFromDir(packageName, dir) {
|
|
2463
|
-
const pkgDir =
|
|
2464
|
-
const pkgJsonPath =
|
|
2530
|
+
const pkgDir = path27.join(dir, "node_modules", ...packageName.split("/"));
|
|
2531
|
+
const pkgJsonPath = path27.join(pkgDir, "package.json");
|
|
2465
2532
|
let pkgJson;
|
|
2466
2533
|
try {
|
|
2467
|
-
pkgJson = JSON.parse(await
|
|
2534
|
+
pkgJson = JSON.parse(await fs29.readFile(pkgJsonPath, "utf-8"));
|
|
2468
2535
|
} catch (err) {
|
|
2469
2536
|
throw new Error(`Cannot read package.json for "${packageName}" at ${pkgJsonPath}: ${err.message}`);
|
|
2470
2537
|
}
|
|
@@ -2477,9 +2544,9 @@ async function importFromDir(packageName, dir) {
|
|
|
2477
2544
|
} else {
|
|
2478
2545
|
entry = pkgJson.main ?? "index.js";
|
|
2479
2546
|
}
|
|
2480
|
-
const entryPath =
|
|
2547
|
+
const entryPath = path27.join(pkgDir, entry);
|
|
2481
2548
|
try {
|
|
2482
|
-
await
|
|
2549
|
+
await fs29.access(entryPath);
|
|
2483
2550
|
} catch {
|
|
2484
2551
|
throw new Error(`Entry point "${entry}" not found for "${packageName}" at ${entryPath}`);
|
|
2485
2552
|
}
|
|
@@ -2489,7 +2556,7 @@ async function installNpmPlugin(packageName, pluginsDir) {
|
|
|
2489
2556
|
if (!VALID_NPM_NAME.test(packageName)) {
|
|
2490
2557
|
throw new Error(`Invalid package name: "${packageName}". Must be a valid npm package name.`);
|
|
2491
2558
|
}
|
|
2492
|
-
const dir = pluginsDir
|
|
2559
|
+
const dir = pluginsDir;
|
|
2493
2560
|
try {
|
|
2494
2561
|
return await importFromDir(packageName, dir);
|
|
2495
2562
|
} catch {
|
|
@@ -2571,10 +2638,10 @@ var install_context_exports = {};
|
|
|
2571
2638
|
__export(install_context_exports, {
|
|
2572
2639
|
createInstallContext: () => createInstallContext
|
|
2573
2640
|
});
|
|
2574
|
-
import
|
|
2641
|
+
import path28 from "path";
|
|
2575
2642
|
function createInstallContext(opts) {
|
|
2576
2643
|
const { pluginName, settingsManager, basePath, instanceRoot } = opts;
|
|
2577
|
-
const dataDir =
|
|
2644
|
+
const dataDir = path28.join(basePath, pluginName, "data");
|
|
2578
2645
|
return {
|
|
2579
2646
|
pluginName,
|
|
2580
2647
|
terminal: createTerminalIO(),
|
|
@@ -2598,21 +2665,31 @@ __export(api_client_exports, {
|
|
|
2598
2665
|
apiCall: () => apiCall,
|
|
2599
2666
|
readApiPort: () => readApiPort,
|
|
2600
2667
|
readApiSecret: () => readApiSecret,
|
|
2601
|
-
removeStalePortFile: () => removeStalePortFile
|
|
2668
|
+
removeStalePortFile: () => removeStalePortFile,
|
|
2669
|
+
waitForPortFile: () => waitForPortFile
|
|
2602
2670
|
});
|
|
2603
|
-
import * as
|
|
2604
|
-
import * as
|
|
2605
|
-
import
|
|
2671
|
+
import * as fs30 from "fs";
|
|
2672
|
+
import * as path29 from "path";
|
|
2673
|
+
import { setTimeout as sleep } from "timers/promises";
|
|
2606
2674
|
function defaultPortFile(root) {
|
|
2607
|
-
return
|
|
2675
|
+
return path29.join(root, "api.port");
|
|
2608
2676
|
}
|
|
2609
2677
|
function defaultSecretFile(root) {
|
|
2610
|
-
return
|
|
2678
|
+
return path29.join(root, "api-secret");
|
|
2679
|
+
}
|
|
2680
|
+
async function waitForPortFile(portFilePath, timeoutMs = 5e3, intervalMs = 100) {
|
|
2681
|
+
const deadline = Date.now() + timeoutMs;
|
|
2682
|
+
while (Date.now() < deadline) {
|
|
2683
|
+
const port = readApiPort(portFilePath);
|
|
2684
|
+
if (port !== null) return port;
|
|
2685
|
+
await sleep(intervalMs);
|
|
2686
|
+
}
|
|
2687
|
+
return readApiPort(portFilePath);
|
|
2611
2688
|
}
|
|
2612
2689
|
function readApiPort(portFilePath, instanceRoot) {
|
|
2613
2690
|
const filePath = portFilePath ?? defaultPortFile(instanceRoot);
|
|
2614
2691
|
try {
|
|
2615
|
-
const content =
|
|
2692
|
+
const content = fs30.readFileSync(filePath, "utf-8").trim();
|
|
2616
2693
|
const port = parseInt(content, 10);
|
|
2617
2694
|
return isNaN(port) ? null : port;
|
|
2618
2695
|
} catch {
|
|
@@ -2622,7 +2699,7 @@ function readApiPort(portFilePath, instanceRoot) {
|
|
|
2622
2699
|
function readApiSecret(secretFilePath, instanceRoot) {
|
|
2623
2700
|
const filePath = secretFilePath ?? defaultSecretFile(instanceRoot);
|
|
2624
2701
|
try {
|
|
2625
|
-
const content =
|
|
2702
|
+
const content = fs30.readFileSync(filePath, "utf-8").trim();
|
|
2626
2703
|
return content || null;
|
|
2627
2704
|
} catch {
|
|
2628
2705
|
return null;
|
|
@@ -2631,7 +2708,7 @@ function readApiSecret(secretFilePath, instanceRoot) {
|
|
|
2631
2708
|
function removeStalePortFile(portFilePath, instanceRoot) {
|
|
2632
2709
|
const filePath = portFilePath ?? defaultPortFile(instanceRoot);
|
|
2633
2710
|
try {
|
|
2634
|
-
|
|
2711
|
+
fs30.unlinkSync(filePath);
|
|
2635
2712
|
} catch {
|
|
2636
2713
|
}
|
|
2637
2714
|
}
|
|
@@ -2643,11 +2720,9 @@ async function apiCall(port, urlPath, options, instanceRoot) {
|
|
|
2643
2720
|
}
|
|
2644
2721
|
return fetch(`http://127.0.0.1:${port}${urlPath}`, { ...options, headers });
|
|
2645
2722
|
}
|
|
2646
|
-
var DEFAULT_ROOT;
|
|
2647
2723
|
var init_api_client = __esm({
|
|
2648
2724
|
"src/cli/api-client.ts"() {
|
|
2649
2725
|
"use strict";
|
|
2650
|
-
DEFAULT_ROOT = path27.join(os13.homedir(), ".openacp");
|
|
2651
2726
|
}
|
|
2652
2727
|
});
|
|
2653
2728
|
|
|
@@ -2681,8 +2756,8 @@ var init_notification = __esm({
|
|
|
2681
2756
|
});
|
|
2682
2757
|
|
|
2683
2758
|
// src/plugins/file-service/file-service.ts
|
|
2684
|
-
import
|
|
2685
|
-
import
|
|
2759
|
+
import fs32 from "fs";
|
|
2760
|
+
import path32 from "path";
|
|
2686
2761
|
import { OggOpusDecoder } from "ogg-opus-decoder";
|
|
2687
2762
|
import wav from "node-wav";
|
|
2688
2763
|
function classifyMime(mimeType) {
|
|
@@ -2738,14 +2813,14 @@ var init_file_service = __esm({
|
|
|
2738
2813
|
const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
|
|
2739
2814
|
let removed = 0;
|
|
2740
2815
|
try {
|
|
2741
|
-
const entries = await
|
|
2816
|
+
const entries = await fs32.promises.readdir(this.baseDir, { withFileTypes: true });
|
|
2742
2817
|
for (const entry of entries) {
|
|
2743
2818
|
if (!entry.isDirectory()) continue;
|
|
2744
|
-
const dirPath =
|
|
2819
|
+
const dirPath = path32.join(this.baseDir, entry.name);
|
|
2745
2820
|
try {
|
|
2746
|
-
const stat = await
|
|
2821
|
+
const stat = await fs32.promises.stat(dirPath);
|
|
2747
2822
|
if (stat.mtimeMs < cutoff) {
|
|
2748
|
-
await
|
|
2823
|
+
await fs32.promises.rm(dirPath, { recursive: true, force: true });
|
|
2749
2824
|
removed++;
|
|
2750
2825
|
}
|
|
2751
2826
|
} catch {
|
|
@@ -2756,11 +2831,11 @@ var init_file_service = __esm({
|
|
|
2756
2831
|
return removed;
|
|
2757
2832
|
}
|
|
2758
2833
|
async saveFile(sessionId, fileName, data, mimeType) {
|
|
2759
|
-
const sessionDir =
|
|
2760
|
-
await
|
|
2834
|
+
const sessionDir = path32.join(this.baseDir, sessionId);
|
|
2835
|
+
await fs32.promises.mkdir(sessionDir, { recursive: true });
|
|
2761
2836
|
const safeName = `${Date.now()}-${fileName.replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
2762
|
-
const filePath =
|
|
2763
|
-
await
|
|
2837
|
+
const filePath = path32.join(sessionDir, safeName);
|
|
2838
|
+
await fs32.promises.writeFile(filePath, data);
|
|
2764
2839
|
return {
|
|
2765
2840
|
type: classifyMime(mimeType),
|
|
2766
2841
|
filePath,
|
|
@@ -2771,14 +2846,14 @@ var init_file_service = __esm({
|
|
|
2771
2846
|
}
|
|
2772
2847
|
async resolveFile(filePath) {
|
|
2773
2848
|
try {
|
|
2774
|
-
const stat = await
|
|
2849
|
+
const stat = await fs32.promises.stat(filePath);
|
|
2775
2850
|
if (!stat.isFile()) return null;
|
|
2776
|
-
const ext =
|
|
2851
|
+
const ext = path32.extname(filePath).toLowerCase();
|
|
2777
2852
|
const mimeType = EXT_TO_MIME[ext] || "application/octet-stream";
|
|
2778
2853
|
return {
|
|
2779
2854
|
type: classifyMime(mimeType),
|
|
2780
2855
|
filePath,
|
|
2781
|
-
fileName:
|
|
2856
|
+
fileName: path32.basename(filePath),
|
|
2782
2857
|
mimeType,
|
|
2783
2858
|
size: stat.size
|
|
2784
2859
|
};
|
|
@@ -3013,7 +3088,7 @@ function createAuthPreHandler(getSecret, getJwtSecret, tokenStore) {
|
|
|
3013
3088
|
const queryToken = request.query?.token;
|
|
3014
3089
|
const token = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : queryToken;
|
|
3015
3090
|
if (queryToken && !authHeader) {
|
|
3016
|
-
|
|
3091
|
+
log21.warn(
|
|
3017
3092
|
{ url: request.url.replace(/([?&]token=)[^&]+/, "$1[REDACTED]") },
|
|
3018
3093
|
"Token passed via URL query param \u2014 use Authorization: Bearer header to avoid token leakage in tunnel/proxy logs"
|
|
3019
3094
|
);
|
|
@@ -3067,7 +3142,7 @@ function requireRole(role) {
|
|
|
3067
3142
|
}
|
|
3068
3143
|
};
|
|
3069
3144
|
}
|
|
3070
|
-
var
|
|
3145
|
+
var log21;
|
|
3071
3146
|
var init_auth = __esm({
|
|
3072
3147
|
"src/plugins/api-server/middleware/auth.ts"() {
|
|
3073
3148
|
"use strict";
|
|
@@ -3075,7 +3150,7 @@ var init_auth = __esm({
|
|
|
3075
3150
|
init_jwt();
|
|
3076
3151
|
init_roles();
|
|
3077
3152
|
init_log();
|
|
3078
|
-
|
|
3153
|
+
log21 = createChildLogger({ module: "api-auth" });
|
|
3079
3154
|
}
|
|
3080
3155
|
});
|
|
3081
3156
|
|
|
@@ -3344,8 +3419,8 @@ data: ${JSON.stringify(data)}
|
|
|
3344
3419
|
});
|
|
3345
3420
|
|
|
3346
3421
|
// src/plugins/api-server/static-server.ts
|
|
3347
|
-
import * as
|
|
3348
|
-
import * as
|
|
3422
|
+
import * as fs33 from "fs";
|
|
3423
|
+
import * as path33 from "path";
|
|
3349
3424
|
import { fileURLToPath } from "url";
|
|
3350
3425
|
var MIME_TYPES, StaticServer;
|
|
3351
3426
|
var init_static_server = __esm({
|
|
@@ -3369,16 +3444,16 @@ var init_static_server = __esm({
|
|
|
3369
3444
|
this.uiDir = uiDir;
|
|
3370
3445
|
if (!this.uiDir) {
|
|
3371
3446
|
const __filename = fileURLToPath(import.meta.url);
|
|
3372
|
-
const candidate =
|
|
3373
|
-
if (
|
|
3447
|
+
const candidate = path33.resolve(path33.dirname(__filename), "../../ui/dist");
|
|
3448
|
+
if (fs33.existsSync(path33.join(candidate, "index.html"))) {
|
|
3374
3449
|
this.uiDir = candidate;
|
|
3375
3450
|
}
|
|
3376
3451
|
if (!this.uiDir) {
|
|
3377
|
-
const publishCandidate =
|
|
3378
|
-
|
|
3452
|
+
const publishCandidate = path33.resolve(
|
|
3453
|
+
path33.dirname(__filename),
|
|
3379
3454
|
"../ui"
|
|
3380
3455
|
);
|
|
3381
|
-
if (
|
|
3456
|
+
if (fs33.existsSync(path33.join(publishCandidate, "index.html"))) {
|
|
3382
3457
|
this.uiDir = publishCandidate;
|
|
3383
3458
|
}
|
|
3384
3459
|
}
|
|
@@ -3390,23 +3465,23 @@ var init_static_server = __esm({
|
|
|
3390
3465
|
serve(req, res) {
|
|
3391
3466
|
if (!this.uiDir) return false;
|
|
3392
3467
|
const urlPath = (req.url || "/").split("?")[0];
|
|
3393
|
-
const safePath =
|
|
3394
|
-
const filePath =
|
|
3395
|
-
if (!filePath.startsWith(this.uiDir +
|
|
3468
|
+
const safePath = path33.normalize(urlPath);
|
|
3469
|
+
const filePath = path33.join(this.uiDir, safePath);
|
|
3470
|
+
if (!filePath.startsWith(this.uiDir + path33.sep) && filePath !== this.uiDir)
|
|
3396
3471
|
return false;
|
|
3397
3472
|
let realFilePath;
|
|
3398
3473
|
try {
|
|
3399
|
-
realFilePath =
|
|
3474
|
+
realFilePath = fs33.realpathSync(filePath);
|
|
3400
3475
|
} catch {
|
|
3401
3476
|
realFilePath = null;
|
|
3402
3477
|
}
|
|
3403
3478
|
if (realFilePath !== null) {
|
|
3404
|
-
const realUiDir =
|
|
3405
|
-
if (!realFilePath.startsWith(realUiDir +
|
|
3479
|
+
const realUiDir = fs33.realpathSync(this.uiDir);
|
|
3480
|
+
if (!realFilePath.startsWith(realUiDir + path33.sep) && realFilePath !== realUiDir)
|
|
3406
3481
|
return false;
|
|
3407
3482
|
}
|
|
3408
|
-
if (realFilePath !== null &&
|
|
3409
|
-
const ext =
|
|
3483
|
+
if (realFilePath !== null && fs33.existsSync(realFilePath) && fs33.statSync(realFilePath).isFile()) {
|
|
3484
|
+
const ext = path33.extname(filePath);
|
|
3410
3485
|
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
3411
3486
|
const isHashed = /\.[a-zA-Z0-9]{8,}\.(js|css)$/.test(filePath);
|
|
3412
3487
|
const cacheControl = isHashed ? "public, max-age=31536000, immutable" : "no-cache";
|
|
@@ -3414,16 +3489,16 @@ var init_static_server = __esm({
|
|
|
3414
3489
|
"Content-Type": contentType,
|
|
3415
3490
|
"Cache-Control": cacheControl
|
|
3416
3491
|
});
|
|
3417
|
-
|
|
3492
|
+
fs33.createReadStream(realFilePath).pipe(res);
|
|
3418
3493
|
return true;
|
|
3419
3494
|
}
|
|
3420
|
-
const indexPath =
|
|
3421
|
-
if (
|
|
3495
|
+
const indexPath = path33.join(this.uiDir, "index.html");
|
|
3496
|
+
if (fs33.existsSync(indexPath)) {
|
|
3422
3497
|
res.writeHead(200, {
|
|
3423
3498
|
"Content-Type": "text/html; charset=utf-8",
|
|
3424
3499
|
"Cache-Control": "no-cache"
|
|
3425
3500
|
});
|
|
3426
|
-
|
|
3501
|
+
fs33.createReadStream(indexPath).pipe(res);
|
|
3427
3502
|
return true;
|
|
3428
3503
|
}
|
|
3429
3504
|
return false;
|
|
@@ -3578,8 +3653,8 @@ var init_exports = __esm({
|
|
|
3578
3653
|
});
|
|
3579
3654
|
|
|
3580
3655
|
// src/plugins/context/context-cache.ts
|
|
3581
|
-
import * as
|
|
3582
|
-
import * as
|
|
3656
|
+
import * as fs34 from "fs";
|
|
3657
|
+
import * as path34 from "path";
|
|
3583
3658
|
import * as crypto2 from "crypto";
|
|
3584
3659
|
var DEFAULT_TTL_MS, ContextCache;
|
|
3585
3660
|
var init_context_cache = __esm({
|
|
@@ -3590,37 +3665,35 @@ var init_context_cache = __esm({
|
|
|
3590
3665
|
constructor(cacheDir, ttlMs = DEFAULT_TTL_MS) {
|
|
3591
3666
|
this.cacheDir = cacheDir;
|
|
3592
3667
|
this.ttlMs = ttlMs;
|
|
3593
|
-
|
|
3668
|
+
fs34.mkdirSync(cacheDir, { recursive: true });
|
|
3594
3669
|
}
|
|
3595
3670
|
keyHash(repoPath, queryKey) {
|
|
3596
3671
|
return crypto2.createHash("sha256").update(`${repoPath}:${queryKey}`).digest("hex").slice(0, 16);
|
|
3597
3672
|
}
|
|
3598
3673
|
filePath(repoPath, queryKey) {
|
|
3599
|
-
return
|
|
3674
|
+
return path34.join(this.cacheDir, `${this.keyHash(repoPath, queryKey)}.json`);
|
|
3600
3675
|
}
|
|
3601
3676
|
get(repoPath, queryKey) {
|
|
3602
3677
|
const fp = this.filePath(repoPath, queryKey);
|
|
3603
3678
|
try {
|
|
3604
|
-
const stat =
|
|
3679
|
+
const stat = fs34.statSync(fp);
|
|
3605
3680
|
if (Date.now() - stat.mtimeMs > this.ttlMs) {
|
|
3606
|
-
|
|
3681
|
+
fs34.unlinkSync(fp);
|
|
3607
3682
|
return null;
|
|
3608
3683
|
}
|
|
3609
|
-
return JSON.parse(
|
|
3684
|
+
return JSON.parse(fs34.readFileSync(fp, "utf-8"));
|
|
3610
3685
|
} catch {
|
|
3611
3686
|
return null;
|
|
3612
3687
|
}
|
|
3613
3688
|
}
|
|
3614
3689
|
set(repoPath, queryKey, result) {
|
|
3615
|
-
|
|
3690
|
+
fs34.writeFileSync(this.filePath(repoPath, queryKey), JSON.stringify(result));
|
|
3616
3691
|
}
|
|
3617
3692
|
};
|
|
3618
3693
|
}
|
|
3619
3694
|
});
|
|
3620
3695
|
|
|
3621
3696
|
// src/plugins/context/context-manager.ts
|
|
3622
|
-
import * as os15 from "os";
|
|
3623
|
-
import * as path33 from "path";
|
|
3624
3697
|
var ContextManager;
|
|
3625
3698
|
var init_context_manager = __esm({
|
|
3626
3699
|
"src/plugins/context/context-manager.ts"() {
|
|
@@ -3632,7 +3705,7 @@ var init_context_manager = __esm({
|
|
|
3632
3705
|
historyStore;
|
|
3633
3706
|
sessionFlusher;
|
|
3634
3707
|
constructor(cachePath) {
|
|
3635
|
-
this.cache = new ContextCache(cachePath
|
|
3708
|
+
this.cache = new ContextCache(cachePath);
|
|
3636
3709
|
}
|
|
3637
3710
|
setHistoryStore(store) {
|
|
3638
3711
|
this.historyStore = store;
|
|
@@ -4549,8 +4622,8 @@ function formatToolSummary(name, rawInput, displaySummary) {
|
|
|
4549
4622
|
}
|
|
4550
4623
|
if (lowerName === "grep") {
|
|
4551
4624
|
const pattern = args.pattern ?? "";
|
|
4552
|
-
const
|
|
4553
|
-
return pattern ? `\u{1F50D} Grep "${pattern}"${
|
|
4625
|
+
const path35 = args.path ?? "";
|
|
4626
|
+
return pattern ? `\u{1F50D} Grep "${pattern}"${path35 ? ` in ${path35}` : ""}` : `\u{1F527} ${name}`;
|
|
4554
4627
|
}
|
|
4555
4628
|
if (lowerName === "glob") {
|
|
4556
4629
|
const pattern = args.pattern ?? "";
|
|
@@ -4586,8 +4659,8 @@ function formatToolTitle(name, rawInput, displayTitle) {
|
|
|
4586
4659
|
}
|
|
4587
4660
|
if (lowerName === "grep") {
|
|
4588
4661
|
const pattern = args.pattern ?? "";
|
|
4589
|
-
const
|
|
4590
|
-
return pattern ? `"${pattern}"${
|
|
4662
|
+
const path35 = args.path ?? "";
|
|
4663
|
+
return pattern ? `"${pattern}"${path35 ? ` in ${path35}` : ""}` : name;
|
|
4591
4664
|
}
|
|
4592
4665
|
if (lowerName === "glob") {
|
|
4593
4666
|
return String(args.pattern ?? name);
|
|
@@ -4975,10 +5048,10 @@ var init_send_queue = __esm({
|
|
|
4975
5048
|
const type = opts?.type ?? "other";
|
|
4976
5049
|
const key = opts?.key;
|
|
4977
5050
|
const category = opts?.category;
|
|
4978
|
-
let
|
|
5051
|
+
let resolve7;
|
|
4979
5052
|
let reject;
|
|
4980
5053
|
const promise = new Promise((res, rej) => {
|
|
4981
|
-
|
|
5054
|
+
resolve7 = res;
|
|
4982
5055
|
reject = rej;
|
|
4983
5056
|
});
|
|
4984
5057
|
promise.catch(() => {
|
|
@@ -4989,12 +5062,12 @@ var init_send_queue = __esm({
|
|
|
4989
5062
|
);
|
|
4990
5063
|
if (idx !== -1) {
|
|
4991
5064
|
this.items[idx].resolve(void 0);
|
|
4992
|
-
this.items[idx] = { fn, type, key, category, resolve:
|
|
5065
|
+
this.items[idx] = { fn, type, key, category, resolve: resolve7, reject, promise };
|
|
4993
5066
|
this.scheduleProcess();
|
|
4994
5067
|
return promise;
|
|
4995
5068
|
}
|
|
4996
5069
|
}
|
|
4997
|
-
this.items.push({ fn, type, key, category, resolve:
|
|
5070
|
+
this.items.push({ fn, type, key, category, resolve: resolve7, reject, promise });
|
|
4998
5071
|
this.scheduleProcess();
|
|
4999
5072
|
return promise;
|
|
5000
5073
|
}
|
|
@@ -5256,7 +5329,7 @@ var init_stream_accumulator = __esm({
|
|
|
5256
5329
|
});
|
|
5257
5330
|
|
|
5258
5331
|
// src/core/adapter-primitives/display-spec-builder.ts
|
|
5259
|
-
function
|
|
5332
|
+
function asRecord3(value) {
|
|
5260
5333
|
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
5261
5334
|
return value;
|
|
5262
5335
|
}
|
|
@@ -5289,8 +5362,21 @@ function parseApplyPatchTargets(patchText) {
|
|
|
5289
5362
|
function buildTitle(entry, kind) {
|
|
5290
5363
|
if (entry.displayTitle) return entry.displayTitle;
|
|
5291
5364
|
if (entry.displaySummary) return entry.displaySummary;
|
|
5292
|
-
const input2 =
|
|
5365
|
+
const input2 = asRecord3(entry.rawInput);
|
|
5293
5366
|
const nameLower = entry.name.toLowerCase();
|
|
5367
|
+
if (isApplyPatchOtherTool(entry.kind, entry.name, entry.rawInput)) {
|
|
5368
|
+
const patchText = getStringField(input2, ["patchText", "patch_text"]);
|
|
5369
|
+
if (patchText) {
|
|
5370
|
+
const targets = parseApplyPatchTargets(patchText);
|
|
5371
|
+
if (targets.length === 1) return targets[0];
|
|
5372
|
+
if (targets.length > 1) {
|
|
5373
|
+
const shown = targets.slice(0, 2).join(", ");
|
|
5374
|
+
const rest = targets.length - 2;
|
|
5375
|
+
return rest > 0 ? `${shown} (+${rest} more)` : shown;
|
|
5376
|
+
}
|
|
5377
|
+
}
|
|
5378
|
+
return "apply_patch";
|
|
5379
|
+
}
|
|
5294
5380
|
if (kind === "read") {
|
|
5295
5381
|
const filePath = getStringField(input2, ["file_path", "filePath", "path"]);
|
|
5296
5382
|
if (filePath) {
|
|
@@ -5339,36 +5425,6 @@ function buildTitle(entry, kind) {
|
|
|
5339
5425
|
}
|
|
5340
5426
|
return capitalize(entry.name);
|
|
5341
5427
|
}
|
|
5342
|
-
if (nameLower === "apply_patch") {
|
|
5343
|
-
const patchText = getStringField(input2, ["patchText", "patch_text"]);
|
|
5344
|
-
if (patchText) {
|
|
5345
|
-
const targets = parseApplyPatchTargets(patchText);
|
|
5346
|
-
if (targets.length === 1) return targets[0];
|
|
5347
|
-
if (targets.length > 1) {
|
|
5348
|
-
const shown = targets.slice(0, 2).join(", ");
|
|
5349
|
-
const remaining = targets.length - 2;
|
|
5350
|
-
return remaining > 0 ? `${shown} (+${remaining} more)` : shown;
|
|
5351
|
-
}
|
|
5352
|
-
}
|
|
5353
|
-
return "apply_patch";
|
|
5354
|
-
}
|
|
5355
|
-
if (nameLower === "todowrite") {
|
|
5356
|
-
const todos = Array.isArray(input2.todos) ? input2.todos : [];
|
|
5357
|
-
if (todos.length > 0) {
|
|
5358
|
-
const inProgress = todos.filter((t) => {
|
|
5359
|
-
if (!t || typeof t !== "object") return false;
|
|
5360
|
-
const status = t.status;
|
|
5361
|
-
return status === "in_progress";
|
|
5362
|
-
}).length;
|
|
5363
|
-
const completed = todos.filter((t) => {
|
|
5364
|
-
if (!t || typeof t !== "object") return false;
|
|
5365
|
-
const status = t.status;
|
|
5366
|
-
return status === "completed";
|
|
5367
|
-
}).length;
|
|
5368
|
-
return `Todo list (${completed}/${todos.length} done${inProgress > 0 ? `, ${inProgress} active` : ""})`;
|
|
5369
|
-
}
|
|
5370
|
-
return "Todo list";
|
|
5371
|
-
}
|
|
5372
5428
|
if (kind === "fetch" || kind === "web") {
|
|
5373
5429
|
const url = typeof input2.url === "string" ? input2.url : null;
|
|
5374
5430
|
if (url && url !== "undefined") return url.length > 60 ? url.slice(0, 57) + "..." : url;
|
|
@@ -5379,19 +5435,6 @@ function buildTitle(entry, kind) {
|
|
|
5379
5435
|
if (nameLower === "skill" && typeof input2.skill === "string" && input2.skill) {
|
|
5380
5436
|
return input2.skill;
|
|
5381
5437
|
}
|
|
5382
|
-
if (nameLower === "apply_patch") {
|
|
5383
|
-
const patchText = getStringField(input2, ["patchText", "patch_text"]);
|
|
5384
|
-
if (patchText) {
|
|
5385
|
-
const targets = parseApplyPatchTargets(patchText);
|
|
5386
|
-
if (targets.length === 1) return targets[0];
|
|
5387
|
-
if (targets.length > 1) {
|
|
5388
|
-
const shown = targets.slice(0, 2).join(", ");
|
|
5389
|
-
const rest = targets.length - 2;
|
|
5390
|
-
return rest > 0 ? `${shown} (+${rest} more)` : shown;
|
|
5391
|
-
}
|
|
5392
|
-
}
|
|
5393
|
-
return "apply_patch";
|
|
5394
|
-
}
|
|
5395
5438
|
if (nameLower === "todowrite") {
|
|
5396
5439
|
const todos = Array.isArray(input2.todos) ? input2.todos : [];
|
|
5397
5440
|
if (todos.length > 0) {
|
|
@@ -5421,6 +5464,7 @@ var init_display_spec_builder = __esm({
|
|
|
5421
5464
|
"src/core/adapter-primitives/display-spec-builder.ts"() {
|
|
5422
5465
|
"use strict";
|
|
5423
5466
|
init_format_types();
|
|
5467
|
+
init_apply_patch_detection();
|
|
5424
5468
|
EXECUTE_KINDS = /* @__PURE__ */ new Set(["execute", "bash", "command", "terminal"]);
|
|
5425
5469
|
INLINE_MAX_LINES = 15;
|
|
5426
5470
|
INLINE_MAX_CHARS = 800;
|
|
@@ -5429,12 +5473,12 @@ var init_display_spec_builder = __esm({
|
|
|
5429
5473
|
this.tunnelService = tunnelService;
|
|
5430
5474
|
}
|
|
5431
5475
|
buildToolSpec(entry, mode, sessionContext) {
|
|
5432
|
-
const effectiveKind = entry.displayKind ?? entry.kind;
|
|
5476
|
+
const effectiveKind = entry.displayKind ?? (isApplyPatchOtherTool(entry.kind, entry.name, entry.rawInput) ? "edit" : entry.kind);
|
|
5433
5477
|
const icon = KIND_ICONS[effectiveKind] ?? KIND_ICONS["other"] ?? "\u{1F6E0}\uFE0F";
|
|
5434
5478
|
const title = buildTitle(entry, effectiveKind);
|
|
5435
5479
|
const isHidden = entry.isNoise && mode !== "high";
|
|
5436
5480
|
const includeMeta = mode !== "low";
|
|
5437
|
-
const input2 =
|
|
5481
|
+
const input2 = asRecord3(entry.rawInput);
|
|
5438
5482
|
const rawDescription = typeof input2.description === "string" ? input2.description : null;
|
|
5439
5483
|
const descLower = rawDescription?.toLowerCase();
|
|
5440
5484
|
const description = includeMeta && rawDescription && rawDescription !== title && descLower !== effectiveKind && descLower !== entry.name.toLowerCase() ? rawDescription : null;
|
|
@@ -5819,14 +5863,14 @@ __export(version_exports, {
|
|
|
5819
5863
|
runUpdate: () => runUpdate
|
|
5820
5864
|
});
|
|
5821
5865
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5822
|
-
import { dirname as
|
|
5823
|
-
import { existsSync as
|
|
5866
|
+
import { dirname as dirname12, join as join16, resolve as resolve6 } from "path";
|
|
5867
|
+
import { existsSync as existsSync16, readFileSync as readFileSync14 } from "fs";
|
|
5824
5868
|
function findPackageJson() {
|
|
5825
|
-
let dir =
|
|
5869
|
+
let dir = dirname12(fileURLToPath2(import.meta.url));
|
|
5826
5870
|
for (let i = 0; i < 5; i++) {
|
|
5827
|
-
const candidate =
|
|
5828
|
-
if (
|
|
5829
|
-
const parent =
|
|
5871
|
+
const candidate = join16(dir, "package.json");
|
|
5872
|
+
if (existsSync16(candidate)) return candidate;
|
|
5873
|
+
const parent = resolve6(dir, "..");
|
|
5830
5874
|
if (parent === dir) break;
|
|
5831
5875
|
dir = parent;
|
|
5832
5876
|
}
|
|
@@ -5836,7 +5880,7 @@ function getCurrentVersion() {
|
|
|
5836
5880
|
try {
|
|
5837
5881
|
const pkgPath = findPackageJson();
|
|
5838
5882
|
if (!pkgPath) return "0.0.0-dev";
|
|
5839
|
-
const pkg = JSON.parse(
|
|
5883
|
+
const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
5840
5884
|
return pkg.version;
|
|
5841
5885
|
} catch {
|
|
5842
5886
|
return "0.0.0-dev";
|
|
@@ -5865,21 +5909,21 @@ function compareVersions(current, latest) {
|
|
|
5865
5909
|
}
|
|
5866
5910
|
async function runUpdate() {
|
|
5867
5911
|
const { spawn: spawn4 } = await import("child_process");
|
|
5868
|
-
return new Promise((
|
|
5912
|
+
return new Promise((resolve7) => {
|
|
5869
5913
|
const child = spawn4("npm", ["install", "-g", `${NPM_PACKAGE}@latest`], {
|
|
5870
5914
|
stdio: "inherit",
|
|
5871
5915
|
shell: true
|
|
5872
5916
|
});
|
|
5873
5917
|
const onSignal = () => {
|
|
5874
5918
|
child.kill("SIGTERM");
|
|
5875
|
-
|
|
5919
|
+
resolve7(false);
|
|
5876
5920
|
};
|
|
5877
5921
|
process.on("SIGINT", onSignal);
|
|
5878
5922
|
process.on("SIGTERM", onSignal);
|
|
5879
5923
|
child.on("close", (code) => {
|
|
5880
5924
|
process.off("SIGINT", onSignal);
|
|
5881
5925
|
process.off("SIGTERM", onSignal);
|
|
5882
|
-
|
|
5926
|
+
resolve7(code === 0);
|
|
5883
5927
|
});
|
|
5884
5928
|
});
|
|
5885
5929
|
}
|
|
@@ -5942,7 +5986,7 @@ function setupDangerousModeCallbacks(bot, core) {
|
|
|
5942
5986
|
}).catch(() => {
|
|
5943
5987
|
});
|
|
5944
5988
|
}
|
|
5945
|
-
|
|
5989
|
+
log23.info({ sessionId, wantOn }, "Bypass permissions toggled via button");
|
|
5946
5990
|
try {
|
|
5947
5991
|
await ctx.editMessageText(buildSessionStatusText(session), {
|
|
5948
5992
|
parse_mode: "HTML",
|
|
@@ -5965,7 +6009,7 @@ function setupDangerousModeCallbacks(bot, core) {
|
|
|
5965
6009
|
const newDangerousMode = !(record.clientOverrides?.bypassPermissions ?? record.dangerousMode ?? false);
|
|
5966
6010
|
core.sessionManager.patchRecord(sessionId, { clientOverrides: { bypassPermissions: newDangerousMode } }).catch(() => {
|
|
5967
6011
|
});
|
|
5968
|
-
|
|
6012
|
+
log23.info(
|
|
5969
6013
|
{ sessionId, dangerousMode: newDangerousMode },
|
|
5970
6014
|
"Bypass permissions toggled via button (store-only, session not in memory)"
|
|
5971
6015
|
);
|
|
@@ -6150,14 +6194,14 @@ async function handleRestart(ctx, core) {
|
|
|
6150
6194
|
await new Promise((r) => setTimeout(r, 500));
|
|
6151
6195
|
await core.requestRestart();
|
|
6152
6196
|
}
|
|
6153
|
-
var
|
|
6197
|
+
var log23, OUTPUT_MODE_LABELS;
|
|
6154
6198
|
var init_admin = __esm({
|
|
6155
6199
|
"src/plugins/telegram/commands/admin.ts"() {
|
|
6156
6200
|
"use strict";
|
|
6157
6201
|
init_bypass_detection();
|
|
6158
6202
|
init_formatting();
|
|
6159
6203
|
init_log();
|
|
6160
|
-
|
|
6204
|
+
log23 = createChildLogger({ module: "telegram-cmd-admin" });
|
|
6161
6205
|
OUTPUT_MODE_LABELS = {
|
|
6162
6206
|
low: "\u{1F507} Low",
|
|
6163
6207
|
medium: "\u{1F4CA} Medium",
|
|
@@ -6172,7 +6216,7 @@ function botFromCtx(ctx) {
|
|
|
6172
6216
|
return { api: ctx.api };
|
|
6173
6217
|
}
|
|
6174
6218
|
async function createSessionDirect(ctx, core, chatId, agentName, workspace, onControlMessage) {
|
|
6175
|
-
|
|
6219
|
+
log24.info({ userId: ctx.from?.id, agentName, workspace }, "New session command (direct)");
|
|
6176
6220
|
let threadId;
|
|
6177
6221
|
try {
|
|
6178
6222
|
const topicName = `\u{1F504} New Session`;
|
|
@@ -6199,7 +6243,7 @@ async function createSessionDirect(ctx, core, chatId, agentName, workspace, onCo
|
|
|
6199
6243
|
onControlMessage?.(session.id, controlMsg.message_id);
|
|
6200
6244
|
return threadId ?? null;
|
|
6201
6245
|
} catch (err) {
|
|
6202
|
-
|
|
6246
|
+
log24.error({ err }, "Session creation failed");
|
|
6203
6247
|
if (threadId) {
|
|
6204
6248
|
try {
|
|
6205
6249
|
await ctx.api.deleteForumTopic(chatId, threadId);
|
|
@@ -6306,10 +6350,8 @@ async function showAgentPicker(ctx, core, chatId) {
|
|
|
6306
6350
|
async function showWorkspacePicker(ctx, core, chatId, agentKey, newMessage = false) {
|
|
6307
6351
|
const records = core.sessionManager.listRecords();
|
|
6308
6352
|
const recentWorkspaces = [...new Set(records.map((r) => r.workingDir).filter(Boolean))].slice(0, 5);
|
|
6309
|
-
const
|
|
6310
|
-
const
|
|
6311
|
-
const resolvedBaseDir = core.configManager.resolveWorkspace(baseDir);
|
|
6312
|
-
const hasBaseDir = recentWorkspaces.some((ws) => ws === baseDir || ws === resolvedBaseDir);
|
|
6353
|
+
const resolvedBaseDir = core.configManager.resolveWorkspace();
|
|
6354
|
+
const hasBaseDir = recentWorkspaces.some((ws) => ws === resolvedBaseDir);
|
|
6313
6355
|
const workspaces = hasBaseDir ? recentWorkspaces : [resolvedBaseDir, ...recentWorkspaces].slice(0, 5);
|
|
6314
6356
|
const kb = new InlineKeyboard2();
|
|
6315
6357
|
for (const ws of workspaces) {
|
|
@@ -6434,7 +6476,7 @@ Agent: <code>${escapeHtml(agentKey)}</code>
|
|
|
6434
6476
|
await _sendCustomPathPrompt(ctx, chatId, agentKey);
|
|
6435
6477
|
});
|
|
6436
6478
|
}
|
|
6437
|
-
var
|
|
6479
|
+
var log24, WS_CACHE_MAX, workspaceCache, nextWsId, _forceReplyMap;
|
|
6438
6480
|
var init_new_session = __esm({
|
|
6439
6481
|
"src/plugins/telegram/commands/new-session.ts"() {
|
|
6440
6482
|
"use strict";
|
|
@@ -6442,7 +6484,7 @@ var init_new_session = __esm({
|
|
|
6442
6484
|
init_topics();
|
|
6443
6485
|
init_log();
|
|
6444
6486
|
init_admin();
|
|
6445
|
-
|
|
6487
|
+
log24 = createChildLogger({ module: "telegram-cmd-new-session" });
|
|
6446
6488
|
WS_CACHE_MAX = 50;
|
|
6447
6489
|
workspaceCache = /* @__PURE__ */ new Map();
|
|
6448
6490
|
nextWsId = 0;
|
|
@@ -6507,7 +6549,7 @@ ${lines.join("\n")}${truncated}`,
|
|
|
6507
6549
|
{ parse_mode: "HTML", reply_markup: keyboard }
|
|
6508
6550
|
);
|
|
6509
6551
|
} catch (err) {
|
|
6510
|
-
|
|
6552
|
+
log25.error({ err }, "handleTopics error");
|
|
6511
6553
|
await ctx.reply("\u274C Failed to list sessions.", { parse_mode: "HTML" }).catch(() => {
|
|
6512
6554
|
});
|
|
6513
6555
|
}
|
|
@@ -6528,13 +6570,13 @@ async function handleCleanup(ctx, core, chatId, statuses) {
|
|
|
6528
6570
|
try {
|
|
6529
6571
|
await ctx.api.deleteForumTopic(chatId, topicId);
|
|
6530
6572
|
} catch (err) {
|
|
6531
|
-
|
|
6573
|
+
log25.warn({ err, sessionId: record.sessionId, topicId }, "Failed to delete forum topic during cleanup");
|
|
6532
6574
|
}
|
|
6533
6575
|
}
|
|
6534
6576
|
await core.sessionManager.removeRecord(record.sessionId);
|
|
6535
6577
|
deleted++;
|
|
6536
6578
|
} catch (err) {
|
|
6537
|
-
|
|
6579
|
+
log25.error({ err, sessionId: record.sessionId }, "Failed to cleanup session");
|
|
6538
6580
|
failed++;
|
|
6539
6581
|
}
|
|
6540
6582
|
}
|
|
@@ -6605,7 +6647,7 @@ async function handleCleanupEverythingConfirmed(ctx, core, chatId, systemTopicId
|
|
|
6605
6647
|
try {
|
|
6606
6648
|
await core.sessionManager.cancelSession(record.sessionId);
|
|
6607
6649
|
} catch (err) {
|
|
6608
|
-
|
|
6650
|
+
log25.warn({ err, sessionId: record.sessionId }, "Failed to cancel session during cleanup");
|
|
6609
6651
|
}
|
|
6610
6652
|
}
|
|
6611
6653
|
const topicId = record.platform?.topicId;
|
|
@@ -6613,13 +6655,13 @@ async function handleCleanupEverythingConfirmed(ctx, core, chatId, systemTopicId
|
|
|
6613
6655
|
try {
|
|
6614
6656
|
await ctx.api.deleteForumTopic(chatId, topicId);
|
|
6615
6657
|
} catch (err) {
|
|
6616
|
-
|
|
6658
|
+
log25.warn({ err, sessionId: record.sessionId, topicId }, "Failed to delete forum topic during cleanup");
|
|
6617
6659
|
}
|
|
6618
6660
|
}
|
|
6619
6661
|
await core.sessionManager.removeRecord(record.sessionId);
|
|
6620
6662
|
deleted++;
|
|
6621
6663
|
} catch (err) {
|
|
6622
|
-
|
|
6664
|
+
log25.error({ err, sessionId: record.sessionId }, "Failed to cleanup session");
|
|
6623
6665
|
failed++;
|
|
6624
6666
|
}
|
|
6625
6667
|
}
|
|
@@ -6687,13 +6729,13 @@ async function handleArchiveConfirm(ctx, core, chatId) {
|
|
|
6687
6729
|
}
|
|
6688
6730
|
}
|
|
6689
6731
|
}
|
|
6690
|
-
var
|
|
6732
|
+
var log25;
|
|
6691
6733
|
var init_session = __esm({
|
|
6692
6734
|
"src/plugins/telegram/commands/session.ts"() {
|
|
6693
6735
|
"use strict";
|
|
6694
6736
|
init_formatting();
|
|
6695
6737
|
init_log();
|
|
6696
|
-
|
|
6738
|
+
log25 = createChildLogger({ module: "telegram-cmd-session" });
|
|
6697
6739
|
}
|
|
6698
6740
|
});
|
|
6699
6741
|
|
|
@@ -6705,14 +6747,14 @@ __export(integrate_exports, {
|
|
|
6705
6747
|
listIntegrations: () => listIntegrations,
|
|
6706
6748
|
uninstallIntegration: () => uninstallIntegration
|
|
6707
6749
|
});
|
|
6708
|
-
import { existsSync as
|
|
6709
|
-
import { join as
|
|
6710
|
-
import { homedir as
|
|
6750
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync11, readFileSync as readFileSync15, writeFileSync as writeFileSync11, unlinkSync as unlinkSync7, chmodSync, rmdirSync } from "fs";
|
|
6751
|
+
import { join as join17, dirname as dirname13 } from "path";
|
|
6752
|
+
import { homedir as homedir4 } from "os";
|
|
6711
6753
|
function isHooksIntegrationSpec(spec) {
|
|
6712
6754
|
return spec.strategy === "hooks";
|
|
6713
6755
|
}
|
|
6714
6756
|
function expandPath(p) {
|
|
6715
|
-
return p.replace(/^~/,
|
|
6757
|
+
return p.replace(/^~/, homedir4());
|
|
6716
6758
|
}
|
|
6717
6759
|
function generateInjectScript(_agentKey, spec) {
|
|
6718
6760
|
const sidVar = spec.sessionIdVar ?? "SESSION_ID";
|
|
@@ -6867,8 +6909,8 @@ function generateOpencodePlugin(spec) {
|
|
|
6867
6909
|
function mergeSettingsJson(settingsPath, hookEvent, hookScriptPath) {
|
|
6868
6910
|
const fullPath = expandPath(settingsPath);
|
|
6869
6911
|
let settings = {};
|
|
6870
|
-
if (
|
|
6871
|
-
const raw =
|
|
6912
|
+
if (existsSync17(fullPath)) {
|
|
6913
|
+
const raw = readFileSync15(fullPath, "utf-8");
|
|
6872
6914
|
writeFileSync11(`${fullPath}.bak`, raw);
|
|
6873
6915
|
settings = JSON.parse(raw);
|
|
6874
6916
|
}
|
|
@@ -6884,14 +6926,14 @@ function mergeSettingsJson(settingsPath, hookEvent, hookScriptPath) {
|
|
|
6884
6926
|
hooks: [{ type: "command", command: hookScriptPath }]
|
|
6885
6927
|
});
|
|
6886
6928
|
}
|
|
6887
|
-
mkdirSync11(
|
|
6929
|
+
mkdirSync11(dirname13(fullPath), { recursive: true });
|
|
6888
6930
|
writeFileSync11(fullPath, JSON.stringify(settings, null, 2) + "\n");
|
|
6889
6931
|
}
|
|
6890
6932
|
function mergeHooksJson(settingsPath, hookEvent, hookScriptPath) {
|
|
6891
6933
|
const fullPath = expandPath(settingsPath);
|
|
6892
6934
|
let config = { version: 1 };
|
|
6893
|
-
if (
|
|
6894
|
-
const raw =
|
|
6935
|
+
if (existsSync17(fullPath)) {
|
|
6936
|
+
const raw = readFileSync15(fullPath, "utf-8");
|
|
6895
6937
|
writeFileSync11(`${fullPath}.bak`, raw);
|
|
6896
6938
|
config = JSON.parse(raw);
|
|
6897
6939
|
}
|
|
@@ -6903,13 +6945,13 @@ function mergeHooksJson(settingsPath, hookEvent, hookScriptPath) {
|
|
|
6903
6945
|
if (!alreadyInstalled) {
|
|
6904
6946
|
eventHooks.push({ command: hookScriptPath });
|
|
6905
6947
|
}
|
|
6906
|
-
mkdirSync11(
|
|
6948
|
+
mkdirSync11(dirname13(fullPath), { recursive: true });
|
|
6907
6949
|
writeFileSync11(fullPath, JSON.stringify(config, null, 2) + "\n");
|
|
6908
6950
|
}
|
|
6909
6951
|
function removeFromSettingsJson(settingsPath, hookEvent) {
|
|
6910
6952
|
const fullPath = expandPath(settingsPath);
|
|
6911
|
-
if (!
|
|
6912
|
-
const raw =
|
|
6953
|
+
if (!existsSync17(fullPath)) return;
|
|
6954
|
+
const raw = readFileSync15(fullPath, "utf-8");
|
|
6913
6955
|
const settings = JSON.parse(raw);
|
|
6914
6956
|
const hooks = settings.hooks;
|
|
6915
6957
|
if (!hooks?.[hookEvent]) return;
|
|
@@ -6923,8 +6965,8 @@ function removeFromSettingsJson(settingsPath, hookEvent) {
|
|
|
6923
6965
|
}
|
|
6924
6966
|
function removeFromHooksJson(settingsPath, hookEvent) {
|
|
6925
6967
|
const fullPath = expandPath(settingsPath);
|
|
6926
|
-
if (!
|
|
6927
|
-
const raw =
|
|
6968
|
+
if (!existsSync17(fullPath)) return;
|
|
6969
|
+
const raw = readFileSync15(fullPath, "utf-8");
|
|
6928
6970
|
const config = JSON.parse(raw);
|
|
6929
6971
|
const hooks = config.hooks;
|
|
6930
6972
|
if (!hooks?.[hookEvent]) return;
|
|
@@ -6947,30 +6989,30 @@ async function installHooksIntegration(agentKey, spec) {
|
|
|
6947
6989
|
}
|
|
6948
6990
|
const hooksDir = expandPath(spec.hooksDirPath);
|
|
6949
6991
|
mkdirSync11(hooksDir, { recursive: true });
|
|
6950
|
-
const injectPath =
|
|
6992
|
+
const injectPath = join17(hooksDir, "openacp-inject-session.sh");
|
|
6951
6993
|
writeFileSync11(injectPath, generateInjectScript(agentKey, spec));
|
|
6952
6994
|
chmodSync(injectPath, 493);
|
|
6953
6995
|
logs.push(`Created ${injectPath}`);
|
|
6954
|
-
const handoffPath =
|
|
6996
|
+
const handoffPath = join17(hooksDir, "openacp-handoff.sh");
|
|
6955
6997
|
writeFileSync11(handoffPath, generateHandoffScript(agentKey));
|
|
6956
6998
|
chmodSync(handoffPath, 493);
|
|
6957
6999
|
logs.push(`Created ${handoffPath}`);
|
|
6958
7000
|
if (spec.commandsPath && spec.handoffCommandName) {
|
|
6959
7001
|
if (spec.commandFormat === "skill") {
|
|
6960
|
-
const skillDir = expandPath(
|
|
7002
|
+
const skillDir = expandPath(join17(spec.commandsPath, spec.handoffCommandName));
|
|
6961
7003
|
mkdirSync11(skillDir, { recursive: true });
|
|
6962
|
-
const skillPath =
|
|
7004
|
+
const skillPath = join17(skillDir, "SKILL.md");
|
|
6963
7005
|
writeFileSync11(skillPath, generateHandoffCommand(agentKey, spec));
|
|
6964
7006
|
logs.push(`Created ${skillPath}`);
|
|
6965
7007
|
} else {
|
|
6966
7008
|
const cmdsDir = expandPath(spec.commandsPath);
|
|
6967
7009
|
mkdirSync11(cmdsDir, { recursive: true });
|
|
6968
|
-
const cmdPath =
|
|
7010
|
+
const cmdPath = join17(cmdsDir, `${spec.handoffCommandName}.md`);
|
|
6969
7011
|
writeFileSync11(cmdPath, generateHandoffCommand(agentKey, spec));
|
|
6970
7012
|
logs.push(`Created ${cmdPath}`);
|
|
6971
7013
|
}
|
|
6972
7014
|
}
|
|
6973
|
-
const injectFullPath =
|
|
7015
|
+
const injectFullPath = join17(hooksDir, "openacp-inject-session.sh");
|
|
6974
7016
|
if (spec.settingsFormat === "hooks_json") {
|
|
6975
7017
|
mergeHooksJson(spec.settingsPath, spec.hookEvent, injectFullPath);
|
|
6976
7018
|
} else {
|
|
@@ -6988,17 +7030,17 @@ async function uninstallHooksIntegration(agentKey, spec) {
|
|
|
6988
7030
|
try {
|
|
6989
7031
|
const hooksDir = expandPath(spec.hooksDirPath);
|
|
6990
7032
|
for (const filename of ["openacp-inject-session.sh", "openacp-handoff.sh"]) {
|
|
6991
|
-
const filePath =
|
|
6992
|
-
if (
|
|
7033
|
+
const filePath = join17(hooksDir, filename);
|
|
7034
|
+
if (existsSync17(filePath)) {
|
|
6993
7035
|
unlinkSync7(filePath);
|
|
6994
7036
|
logs.push(`Removed ${filePath}`);
|
|
6995
7037
|
}
|
|
6996
7038
|
}
|
|
6997
7039
|
if (spec.commandsPath && spec.handoffCommandName) {
|
|
6998
7040
|
if (spec.commandFormat === "skill") {
|
|
6999
|
-
const skillDir = expandPath(
|
|
7000
|
-
const skillPath =
|
|
7001
|
-
if (
|
|
7041
|
+
const skillDir = expandPath(join17(spec.commandsPath, spec.handoffCommandName));
|
|
7042
|
+
const skillPath = join17(skillDir, "SKILL.md");
|
|
7043
|
+
if (existsSync17(skillPath)) {
|
|
7002
7044
|
unlinkSync7(skillPath);
|
|
7003
7045
|
try {
|
|
7004
7046
|
rmdirSync(skillDir);
|
|
@@ -7007,8 +7049,8 @@ async function uninstallHooksIntegration(agentKey, spec) {
|
|
|
7007
7049
|
logs.push(`Removed ${skillPath}`);
|
|
7008
7050
|
}
|
|
7009
7051
|
} else {
|
|
7010
|
-
const cmdPath = expandPath(
|
|
7011
|
-
if (
|
|
7052
|
+
const cmdPath = expandPath(join17(spec.commandsPath, `${spec.handoffCommandName}.md`));
|
|
7053
|
+
if (existsSync17(cmdPath)) {
|
|
7012
7054
|
unlinkSync7(cmdPath);
|
|
7013
7055
|
logs.push(`Removed ${cmdPath}`);
|
|
7014
7056
|
}
|
|
@@ -7031,15 +7073,15 @@ async function installPluginIntegration(_agentKey, spec) {
|
|
|
7031
7073
|
try {
|
|
7032
7074
|
const commandsDir = expandPath(spec.commandsPath);
|
|
7033
7075
|
mkdirSync11(commandsDir, { recursive: true });
|
|
7034
|
-
const commandPath =
|
|
7076
|
+
const commandPath = join17(commandsDir, spec.handoffCommandFile);
|
|
7035
7077
|
const pluginsDir = expandPath(spec.pluginsPath);
|
|
7036
7078
|
mkdirSync11(pluginsDir, { recursive: true });
|
|
7037
|
-
const pluginPath =
|
|
7038
|
-
if (
|
|
7079
|
+
const pluginPath = join17(pluginsDir, spec.pluginFileName);
|
|
7080
|
+
if (existsSync17(commandPath) && existsSync17(pluginPath)) {
|
|
7039
7081
|
logs.push("Already installed, skipping.");
|
|
7040
7082
|
return { success: true, logs };
|
|
7041
7083
|
}
|
|
7042
|
-
if (
|
|
7084
|
+
if (existsSync17(commandPath) || existsSync17(pluginPath)) {
|
|
7043
7085
|
logs.push("Overwriting existing files.");
|
|
7044
7086
|
}
|
|
7045
7087
|
writeFileSync11(commandPath, generateOpencodeHandoffCommand(spec));
|
|
@@ -7055,15 +7097,15 @@ async function installPluginIntegration(_agentKey, spec) {
|
|
|
7055
7097
|
async function uninstallPluginIntegration(_agentKey, spec) {
|
|
7056
7098
|
const logs = [];
|
|
7057
7099
|
try {
|
|
7058
|
-
const commandPath =
|
|
7100
|
+
const commandPath = join17(expandPath(spec.commandsPath), spec.handoffCommandFile);
|
|
7059
7101
|
let removedCount = 0;
|
|
7060
|
-
if (
|
|
7102
|
+
if (existsSync17(commandPath)) {
|
|
7061
7103
|
unlinkSync7(commandPath);
|
|
7062
7104
|
logs.push(`Removed ${commandPath}`);
|
|
7063
7105
|
removedCount += 1;
|
|
7064
7106
|
}
|
|
7065
|
-
const pluginPath =
|
|
7066
|
-
if (
|
|
7107
|
+
const pluginPath = join17(expandPath(spec.pluginsPath), spec.pluginFileName);
|
|
7108
|
+
if (existsSync17(pluginPath)) {
|
|
7067
7109
|
unlinkSync7(pluginPath);
|
|
7068
7110
|
logs.push(`Removed ${pluginPath}`);
|
|
7069
7111
|
removedCount += 1;
|
|
@@ -7097,11 +7139,11 @@ function buildHandoffItem(agentKey, spec) {
|
|
|
7097
7139
|
isInstalled() {
|
|
7098
7140
|
if (isHooksIntegrationSpec(spec)) {
|
|
7099
7141
|
const hooksDir = expandPath(spec.hooksDirPath);
|
|
7100
|
-
return
|
|
7142
|
+
return existsSync17(join17(hooksDir, "openacp-inject-session.sh")) && existsSync17(join17(hooksDir, "openacp-handoff.sh"));
|
|
7101
7143
|
}
|
|
7102
|
-
const commandPath =
|
|
7103
|
-
const pluginPath =
|
|
7104
|
-
return
|
|
7144
|
+
const commandPath = join17(expandPath(spec.commandsPath), spec.handoffCommandFile);
|
|
7145
|
+
const pluginPath = join17(expandPath(spec.pluginsPath), spec.pluginFileName);
|
|
7146
|
+
return existsSync17(commandPath) && existsSync17(pluginPath);
|
|
7105
7147
|
},
|
|
7106
7148
|
install: () => installIntegration(agentKey, spec),
|
|
7107
7149
|
uninstall: () => uninstallIntegration(agentKey, spec)
|
|
@@ -7116,20 +7158,20 @@ function buildTunnelItem(spec) {
|
|
|
7116
7158
|
if (!isHooksIntegrationSpec(spec) || !spec.commandsPath) return null;
|
|
7117
7159
|
const hooksSpec = spec;
|
|
7118
7160
|
function getTunnelPath() {
|
|
7119
|
-
return
|
|
7161
|
+
return join17(getSkillBasePath(hooksSpec), "openacp-tunnel", "SKILL.md");
|
|
7120
7162
|
}
|
|
7121
7163
|
return {
|
|
7122
7164
|
id: "tunnel",
|
|
7123
7165
|
name: "Tunnel",
|
|
7124
7166
|
description: "Expose local ports to the internet via OpenACP tunnel",
|
|
7125
7167
|
isInstalled() {
|
|
7126
|
-
return
|
|
7168
|
+
return existsSync17(getTunnelPath());
|
|
7127
7169
|
},
|
|
7128
7170
|
async install() {
|
|
7129
7171
|
const logs = [];
|
|
7130
7172
|
try {
|
|
7131
7173
|
const skillPath = getTunnelPath();
|
|
7132
|
-
mkdirSync11(
|
|
7174
|
+
mkdirSync11(dirname13(skillPath), { recursive: true });
|
|
7133
7175
|
writeFileSync11(skillPath, generateTunnelCommand());
|
|
7134
7176
|
logs.push(`Created ${skillPath}`);
|
|
7135
7177
|
return { success: true, logs };
|
|
@@ -7142,10 +7184,10 @@ function buildTunnelItem(spec) {
|
|
|
7142
7184
|
const logs = [];
|
|
7143
7185
|
try {
|
|
7144
7186
|
const skillPath = getTunnelPath();
|
|
7145
|
-
if (
|
|
7187
|
+
if (existsSync17(skillPath)) {
|
|
7146
7188
|
unlinkSync7(skillPath);
|
|
7147
7189
|
try {
|
|
7148
|
-
rmdirSync(
|
|
7190
|
+
rmdirSync(dirname13(skillPath));
|
|
7149
7191
|
} catch {
|
|
7150
7192
|
}
|
|
7151
7193
|
logs.push(`Removed ${skillPath}`);
|
|
@@ -7444,7 +7486,7 @@ var init_agents2 = __esm({
|
|
|
7444
7486
|
// src/plugins/telegram/commands/resume.ts
|
|
7445
7487
|
function setupResumeCallbacks(_bot, _core, _chatId, _onControlMessage) {
|
|
7446
7488
|
}
|
|
7447
|
-
var
|
|
7489
|
+
var log26;
|
|
7448
7490
|
var init_resume = __esm({
|
|
7449
7491
|
"src/plugins/telegram/commands/resume.ts"() {
|
|
7450
7492
|
"use strict";
|
|
@@ -7454,7 +7496,7 @@ var init_resume = __esm({
|
|
|
7454
7496
|
init_topics();
|
|
7455
7497
|
init_admin();
|
|
7456
7498
|
init_log();
|
|
7457
|
-
|
|
7499
|
+
log26 = createChildLogger({ module: "telegram-cmd-resume" });
|
|
7458
7500
|
}
|
|
7459
7501
|
});
|
|
7460
7502
|
|
|
@@ -7617,7 +7659,7 @@ function setupSettingsCallbacks(bot, core, getAssistantSession) {
|
|
|
7617
7659
|
} catch {
|
|
7618
7660
|
}
|
|
7619
7661
|
} catch (err) {
|
|
7620
|
-
|
|
7662
|
+
log27.error({ err, fieldPath }, "Failed to toggle config");
|
|
7621
7663
|
try {
|
|
7622
7664
|
await ctx.answerCallbackQuery({ text: "\u274C Failed to update" });
|
|
7623
7665
|
} catch {
|
|
@@ -7698,7 +7740,7 @@ Tap to change:`, {
|
|
|
7698
7740
|
} catch {
|
|
7699
7741
|
}
|
|
7700
7742
|
} catch (err) {
|
|
7701
|
-
|
|
7743
|
+
log27.error({ err, fieldPath }, "Failed to set config");
|
|
7702
7744
|
try {
|
|
7703
7745
|
await ctx.answerCallbackQuery({ text: "\u274C Failed to update" });
|
|
7704
7746
|
} catch {
|
|
@@ -7756,13 +7798,13 @@ Tap to change:`, {
|
|
|
7756
7798
|
}
|
|
7757
7799
|
});
|
|
7758
7800
|
}
|
|
7759
|
-
var
|
|
7801
|
+
var log27;
|
|
7760
7802
|
var init_settings = __esm({
|
|
7761
7803
|
"src/plugins/telegram/commands/settings.ts"() {
|
|
7762
7804
|
"use strict";
|
|
7763
7805
|
init_config_registry();
|
|
7764
7806
|
init_log();
|
|
7765
|
-
|
|
7807
|
+
log27 = createChildLogger({ module: "telegram-settings" });
|
|
7766
7808
|
}
|
|
7767
7809
|
});
|
|
7768
7810
|
|
|
@@ -7809,7 +7851,7 @@ async function handleDoctor(ctx) {
|
|
|
7809
7851
|
reply_markup: keyboard
|
|
7810
7852
|
});
|
|
7811
7853
|
} catch (err) {
|
|
7812
|
-
|
|
7854
|
+
log28.error({ err }, "Doctor command failed");
|
|
7813
7855
|
await ctx.api.editMessageText(
|
|
7814
7856
|
ctx.chat.id,
|
|
7815
7857
|
statusMsg.message_id,
|
|
@@ -7858,7 +7900,7 @@ function setupDoctorCallbacks(bot) {
|
|
|
7858
7900
|
}
|
|
7859
7901
|
}
|
|
7860
7902
|
} catch (err) {
|
|
7861
|
-
|
|
7903
|
+
log28.error({ err, index }, "Doctor fix callback failed");
|
|
7862
7904
|
}
|
|
7863
7905
|
});
|
|
7864
7906
|
bot.callbackQuery("m:doctor", async (ctx) => {
|
|
@@ -7869,13 +7911,13 @@ function setupDoctorCallbacks(bot) {
|
|
|
7869
7911
|
await handleDoctor(ctx);
|
|
7870
7912
|
});
|
|
7871
7913
|
}
|
|
7872
|
-
var
|
|
7914
|
+
var log28, pendingFixesStore;
|
|
7873
7915
|
var init_doctor2 = __esm({
|
|
7874
7916
|
"src/plugins/telegram/commands/doctor.ts"() {
|
|
7875
7917
|
"use strict";
|
|
7876
7918
|
init_doctor();
|
|
7877
7919
|
init_log();
|
|
7878
|
-
|
|
7920
|
+
log28 = createChildLogger({ module: "telegram-cmd-doctor" });
|
|
7879
7921
|
pendingFixesStore = /* @__PURE__ */ new Map();
|
|
7880
7922
|
}
|
|
7881
7923
|
});
|
|
@@ -7934,13 +7976,13 @@ function setupTunnelCallbacks(bot, core) {
|
|
|
7934
7976
|
}
|
|
7935
7977
|
});
|
|
7936
7978
|
}
|
|
7937
|
-
var
|
|
7979
|
+
var log29;
|
|
7938
7980
|
var init_tunnel2 = __esm({
|
|
7939
7981
|
"src/plugins/telegram/commands/tunnel.ts"() {
|
|
7940
7982
|
"use strict";
|
|
7941
7983
|
init_formatting();
|
|
7942
7984
|
init_log();
|
|
7943
|
-
|
|
7985
|
+
log29 = createChildLogger({ module: "telegram-cmd-tunnel" });
|
|
7944
7986
|
}
|
|
7945
7987
|
});
|
|
7946
7988
|
|
|
@@ -7954,10 +7996,10 @@ async function executeSwitchAgent(ctx, core, sessionId, agentName) {
|
|
|
7954
7996
|
`Switched to <b>${escapeHtml(agentName)}</b> (${status})`,
|
|
7955
7997
|
{ parse_mode: "HTML" }
|
|
7956
7998
|
);
|
|
7957
|
-
|
|
7999
|
+
log30.info({ sessionId, agentName, resumed }, "Agent switched via /switch");
|
|
7958
8000
|
} catch (err) {
|
|
7959
8001
|
await ctx.reply(`Failed to switch agent: ${escapeHtml(String(err.message || err))}`);
|
|
7960
|
-
|
|
8002
|
+
log30.warn({ sessionId, agentName, err: err.message }, "Agent switch failed");
|
|
7961
8003
|
}
|
|
7962
8004
|
}
|
|
7963
8005
|
function setupSwitchCallbacks(bot, core) {
|
|
@@ -8002,13 +8044,13 @@ Switch to <b>${escapeHtml(agentName)}</b> anyway?`,
|
|
|
8002
8044
|
await executeSwitchAgent(ctx, core, session.id, data);
|
|
8003
8045
|
});
|
|
8004
8046
|
}
|
|
8005
|
-
var
|
|
8047
|
+
var log30;
|
|
8006
8048
|
var init_switch = __esm({
|
|
8007
8049
|
"src/plugins/telegram/commands/switch.ts"() {
|
|
8008
8050
|
"use strict";
|
|
8009
8051
|
init_formatting();
|
|
8010
8052
|
init_log();
|
|
8011
|
-
|
|
8053
|
+
log30 = createChildLogger({ module: "telegram-cmd-switch" });
|
|
8012
8054
|
}
|
|
8013
8055
|
});
|
|
8014
8056
|
|
|
@@ -8206,7 +8248,7 @@ function setupAllCallbacks(bot, core, chatId, systemTopicIds, getAssistantSessio
|
|
|
8206
8248
|
core,
|
|
8207
8249
|
chatId,
|
|
8208
8250
|
agentKey,
|
|
8209
|
-
core.configManager.
|
|
8251
|
+
core.configManager.resolveWorkspace()
|
|
8210
8252
|
);
|
|
8211
8253
|
});
|
|
8212
8254
|
setupNewSessionCallbacks(bot, core, chatId);
|
|
@@ -8221,7 +8263,7 @@ function setupAllCallbacks(bot, core, chatId, systemTopicIds, getAssistantSessio
|
|
|
8221
8263
|
if (!menuRegistry) return;
|
|
8222
8264
|
const item = menuRegistry.getItem(itemId);
|
|
8223
8265
|
if (!item) {
|
|
8224
|
-
|
|
8266
|
+
log31.warn({ itemId }, "Menu item not found in registry");
|
|
8225
8267
|
return;
|
|
8226
8268
|
}
|
|
8227
8269
|
const topicId = ctx.callbackQuery.message?.message_thread_id;
|
|
@@ -8307,7 +8349,7 @@ ${lines}`, { parse_mode: "HTML" }).catch(() => {
|
|
|
8307
8349
|
}
|
|
8308
8350
|
});
|
|
8309
8351
|
}
|
|
8310
|
-
var
|
|
8352
|
+
var log31, STATIC_COMMANDS;
|
|
8311
8353
|
var init_commands = __esm({
|
|
8312
8354
|
"src/plugins/telegram/commands/index.ts"() {
|
|
8313
8355
|
"use strict";
|
|
@@ -8332,7 +8374,7 @@ var init_commands = __esm({
|
|
|
8332
8374
|
init_settings();
|
|
8333
8375
|
init_doctor2();
|
|
8334
8376
|
init_resume();
|
|
8335
|
-
|
|
8377
|
+
log31 = createChildLogger({ module: "telegram-menu-callbacks" });
|
|
8336
8378
|
STATIC_COMMANDS = [
|
|
8337
8379
|
{ command: "new", description: "Create new session" },
|
|
8338
8380
|
{ command: "newchat", description: "New chat, same agent & workspace" },
|
|
@@ -8367,14 +8409,14 @@ var init_commands = __esm({
|
|
|
8367
8409
|
// src/plugins/telegram/permissions.ts
|
|
8368
8410
|
import { InlineKeyboard as InlineKeyboard11 } from "grammy";
|
|
8369
8411
|
import { nanoid as nanoid4 } from "nanoid";
|
|
8370
|
-
var
|
|
8412
|
+
var log32, PermissionHandler;
|
|
8371
8413
|
var init_permissions = __esm({
|
|
8372
8414
|
"src/plugins/telegram/permissions.ts"() {
|
|
8373
8415
|
"use strict";
|
|
8374
8416
|
init_formatting();
|
|
8375
8417
|
init_topics();
|
|
8376
8418
|
init_log();
|
|
8377
|
-
|
|
8419
|
+
log32 = createChildLogger({ module: "telegram-permissions" });
|
|
8378
8420
|
PermissionHandler = class {
|
|
8379
8421
|
constructor(bot, chatId, getSession, sendNotification) {
|
|
8380
8422
|
this.bot = bot;
|
|
@@ -8434,7 +8476,7 @@ ${escapeHtml(request.description)}`,
|
|
|
8434
8476
|
}
|
|
8435
8477
|
const session = this.getSession(pending.sessionId);
|
|
8436
8478
|
const isAllow = pending.options.find((o) => o.id === optionId)?.isAllow ?? false;
|
|
8437
|
-
|
|
8479
|
+
log32.info({ requestId: pending.requestId, optionId, isAllow }, "Permission responded");
|
|
8438
8480
|
if (session?.permissionGate.requestId === pending.requestId) {
|
|
8439
8481
|
session.permissionGate.resolve(optionId);
|
|
8440
8482
|
}
|
|
@@ -8454,7 +8496,7 @@ ${escapeHtml(request.description)}`,
|
|
|
8454
8496
|
});
|
|
8455
8497
|
|
|
8456
8498
|
// src/plugins/telegram/activity.ts
|
|
8457
|
-
var
|
|
8499
|
+
var log33, THINKING_REFRESH_MS, THINKING_MAX_MS, ThinkingIndicator, ToolCard, ActivityTracker2;
|
|
8458
8500
|
var init_activity = __esm({
|
|
8459
8501
|
"src/plugins/telegram/activity.ts"() {
|
|
8460
8502
|
"use strict";
|
|
@@ -8464,7 +8506,7 @@ var init_activity = __esm({
|
|
|
8464
8506
|
init_stream_accumulator();
|
|
8465
8507
|
init_stream_accumulator();
|
|
8466
8508
|
init_display_spec_builder();
|
|
8467
|
-
|
|
8509
|
+
log33 = createChildLogger({ module: "telegram:activity" });
|
|
8468
8510
|
THINKING_REFRESH_MS = 15e3;
|
|
8469
8511
|
THINKING_MAX_MS = 3 * 60 * 1e3;
|
|
8470
8512
|
ThinkingIndicator = class {
|
|
@@ -8505,7 +8547,7 @@ var init_activity = __esm({
|
|
|
8505
8547
|
}
|
|
8506
8548
|
}
|
|
8507
8549
|
} catch (err) {
|
|
8508
|
-
|
|
8550
|
+
log33.warn({ err }, "ThinkingIndicator.show() failed");
|
|
8509
8551
|
} finally {
|
|
8510
8552
|
this.sending = false;
|
|
8511
8553
|
}
|
|
@@ -8673,7 +8715,7 @@ var init_activity = __esm({
|
|
|
8673
8715
|
this.tracer?.log("telegram", { action: "telegram:delete:overflow", sessionId: this.sessionId, msgId: staleId });
|
|
8674
8716
|
}
|
|
8675
8717
|
} catch (err) {
|
|
8676
|
-
|
|
8718
|
+
log33.warn({ err }, "[ToolCard] send/edit failed");
|
|
8677
8719
|
}
|
|
8678
8720
|
}
|
|
8679
8721
|
};
|
|
@@ -8777,7 +8819,7 @@ var init_activity = __esm({
|
|
|
8777
8819
|
const entry = this.toolStateMap.merge(id, status, rawInput, content, viewerLinks, diffStats);
|
|
8778
8820
|
if (!existed || !entry) return;
|
|
8779
8821
|
if (viewerLinks || entry.viewerLinks) {
|
|
8780
|
-
|
|
8822
|
+
log33.debug({ toolId: id, status, hasIncomingLinks: !!viewerLinks, hasEntryLinks: !!entry.viewerLinks, entryLinks: entry.viewerLinks }, "toolUpdate: viewer links trace");
|
|
8781
8823
|
}
|
|
8782
8824
|
const spec = this.specBuilder.buildToolSpec(entry, this._outputMode, this.sessionContext);
|
|
8783
8825
|
this.toolCard.updateFromSpec(spec);
|
|
@@ -9096,13 +9138,13 @@ var init_draft_manager = __esm({
|
|
|
9096
9138
|
});
|
|
9097
9139
|
|
|
9098
9140
|
// src/plugins/telegram/skill-command-manager.ts
|
|
9099
|
-
var
|
|
9141
|
+
var log34, SkillCommandManager;
|
|
9100
9142
|
var init_skill_command_manager = __esm({
|
|
9101
9143
|
"src/plugins/telegram/skill-command-manager.ts"() {
|
|
9102
9144
|
"use strict";
|
|
9103
9145
|
init_commands();
|
|
9104
9146
|
init_log();
|
|
9105
|
-
|
|
9147
|
+
log34 = createChildLogger({ module: "skill-commands" });
|
|
9106
9148
|
SkillCommandManager = class {
|
|
9107
9149
|
// sessionId → pinned msgId
|
|
9108
9150
|
constructor(bot, chatId, sendQueue, sessionManager) {
|
|
@@ -9168,7 +9210,7 @@ var init_skill_command_manager = __esm({
|
|
|
9168
9210
|
disable_notification: true
|
|
9169
9211
|
});
|
|
9170
9212
|
} catch (err) {
|
|
9171
|
-
|
|
9213
|
+
log34.error({ err, sessionId }, "Failed to send skill commands");
|
|
9172
9214
|
}
|
|
9173
9215
|
}
|
|
9174
9216
|
async cleanup(sessionId) {
|
|
@@ -9347,7 +9389,7 @@ async function validateBotAdmin(token, chatId) {
|
|
|
9347
9389
|
};
|
|
9348
9390
|
}
|
|
9349
9391
|
const { status } = data.result;
|
|
9350
|
-
|
|
9392
|
+
log35.info(
|
|
9351
9393
|
{ status, can_manage_topics: data.result.can_manage_topics, raw_result: data.result },
|
|
9352
9394
|
"validateBotAdmin: getChatMember raw result"
|
|
9353
9395
|
);
|
|
@@ -9367,7 +9409,7 @@ async function validateBotAdmin(token, chatId) {
|
|
|
9367
9409
|
}
|
|
9368
9410
|
async function checkTopicsPrerequisites(token, chatId) {
|
|
9369
9411
|
const issues = [];
|
|
9370
|
-
|
|
9412
|
+
log35.info({ chatId }, "checkTopicsPrerequisites: starting checks");
|
|
9371
9413
|
try {
|
|
9372
9414
|
const res = await fetch(`https://api.telegram.org/bot${token}/getChat`, {
|
|
9373
9415
|
method: "POST",
|
|
@@ -9375,7 +9417,7 @@ async function checkTopicsPrerequisites(token, chatId) {
|
|
|
9375
9417
|
body: JSON.stringify({ chat_id: chatId })
|
|
9376
9418
|
});
|
|
9377
9419
|
const data = await res.json();
|
|
9378
|
-
|
|
9420
|
+
log35.info(
|
|
9379
9421
|
{ chatId, apiOk: data.ok, is_forum: data.result?.is_forum, type: data.result?.type, title: data.result?.title },
|
|
9380
9422
|
"checkTopicsPrerequisites: getChat result"
|
|
9381
9423
|
);
|
|
@@ -9385,11 +9427,11 @@ async function checkTopicsPrerequisites(token, chatId) {
|
|
|
9385
9427
|
);
|
|
9386
9428
|
}
|
|
9387
9429
|
} catch (err) {
|
|
9388
|
-
|
|
9430
|
+
log35.warn({ err, chatId }, "checkTopicsPrerequisites: getChat failed (network error)");
|
|
9389
9431
|
issues.push("\u274C Could not check if Topics are enabled (network error).");
|
|
9390
9432
|
}
|
|
9391
9433
|
const adminResult = await validateBotAdmin(token, chatId);
|
|
9392
|
-
|
|
9434
|
+
log35.info(
|
|
9393
9435
|
{ chatId, adminOk: adminResult.ok, canManageTopics: adminResult.ok ? adminResult.canManageTopics : void 0, error: !adminResult.ok ? adminResult.error : void 0 },
|
|
9394
9436
|
"checkTopicsPrerequisites: validateBotAdmin result"
|
|
9395
9437
|
);
|
|
@@ -9403,16 +9445,16 @@ async function checkTopicsPrerequisites(token, chatId) {
|
|
|
9403
9445
|
'\u274C Bot cannot manage topics.\n\u2192 In Admin settings, enable the "Manage Topics" permission'
|
|
9404
9446
|
);
|
|
9405
9447
|
}
|
|
9406
|
-
|
|
9448
|
+
log35.info({ chatId, issueCount: issues.length, ok: issues.length === 0 }, "checkTopicsPrerequisites: result");
|
|
9407
9449
|
if (issues.length > 0) return { ok: false, issues };
|
|
9408
9450
|
return { ok: true };
|
|
9409
9451
|
}
|
|
9410
|
-
var
|
|
9452
|
+
var log35;
|
|
9411
9453
|
var init_validators = __esm({
|
|
9412
9454
|
"src/plugins/telegram/validators.ts"() {
|
|
9413
9455
|
"use strict";
|
|
9414
9456
|
init_log();
|
|
9415
|
-
|
|
9457
|
+
log35 = createChildLogger({ module: "telegram-validators" });
|
|
9416
9458
|
}
|
|
9417
9459
|
});
|
|
9418
9460
|
|
|
@@ -9431,7 +9473,7 @@ function patchedFetch(input2, init) {
|
|
|
9431
9473
|
}
|
|
9432
9474
|
return fetch(input2, init);
|
|
9433
9475
|
}
|
|
9434
|
-
var
|
|
9476
|
+
var log36, TelegramAdapter;
|
|
9435
9477
|
var init_adapter = __esm({
|
|
9436
9478
|
"src/plugins/telegram/adapter.ts"() {
|
|
9437
9479
|
"use strict";
|
|
@@ -9451,7 +9493,7 @@ var init_adapter = __esm({
|
|
|
9451
9493
|
init_messaging_adapter();
|
|
9452
9494
|
init_renderer2();
|
|
9453
9495
|
init_output_mode_resolver();
|
|
9454
|
-
|
|
9496
|
+
log36 = createChildLogger({ module: "telegram" });
|
|
9455
9497
|
TelegramAdapter = class extends MessagingAdapter {
|
|
9456
9498
|
name = "telegram";
|
|
9457
9499
|
renderer = new TelegramRenderer();
|
|
@@ -9578,7 +9620,7 @@ var init_adapter = __esm({
|
|
|
9578
9620
|
);
|
|
9579
9621
|
this.bot.catch((err) => {
|
|
9580
9622
|
const rootCause = err.error instanceof Error ? err.error : err;
|
|
9581
|
-
|
|
9623
|
+
log36.error({ err: rootCause }, "Telegram bot error");
|
|
9582
9624
|
});
|
|
9583
9625
|
this.bot.api.config.use(async (prev, method, payload, signal) => {
|
|
9584
9626
|
const maxRetries = 3;
|
|
@@ -9596,7 +9638,7 @@ var init_adapter = __esm({
|
|
|
9596
9638
|
if (rateLimitedMethods.includes(method)) {
|
|
9597
9639
|
this.sendQueue.onRateLimited();
|
|
9598
9640
|
}
|
|
9599
|
-
|
|
9641
|
+
log36.warn(
|
|
9600
9642
|
{ method, retryAfter, attempt: attempt + 1 },
|
|
9601
9643
|
"Rate limited by Telegram, retrying"
|
|
9602
9644
|
);
|
|
@@ -9784,9 +9826,9 @@ ${p}` : p;
|
|
|
9784
9826
|
this.setupRoutes();
|
|
9785
9827
|
this.bot.start({
|
|
9786
9828
|
allowed_updates: ["message", "callback_query"],
|
|
9787
|
-
onStart: () =>
|
|
9829
|
+
onStart: () => log36.info({ chatId: this.telegramConfig.chatId }, "Telegram bot started")
|
|
9788
9830
|
});
|
|
9789
|
-
|
|
9831
|
+
log36.info(
|
|
9790
9832
|
{
|
|
9791
9833
|
chatId: this.telegramConfig.chatId,
|
|
9792
9834
|
notificationTopicId: this.telegramConfig.notificationTopicId,
|
|
@@ -9800,12 +9842,12 @@ ${p}` : p;
|
|
|
9800
9842
|
this.telegramConfig.chatId
|
|
9801
9843
|
);
|
|
9802
9844
|
if (prereqResult.ok) {
|
|
9803
|
-
|
|
9845
|
+
log36.info("Telegram adapter: prerequisites OK, initializing topic-dependent features");
|
|
9804
9846
|
await this.initTopicDependentFeatures();
|
|
9805
9847
|
} else {
|
|
9806
|
-
|
|
9848
|
+
log36.warn({ issues: prereqResult.issues }, "Telegram adapter: prerequisites NOT met, starting watcher");
|
|
9807
9849
|
for (const issue of prereqResult.issues) {
|
|
9808
|
-
|
|
9850
|
+
log36.warn({ issue }, "Telegram prerequisite not met");
|
|
9809
9851
|
}
|
|
9810
9852
|
this.startPrerequisiteWatcher(prereqResult.issues);
|
|
9811
9853
|
}
|
|
@@ -9821,7 +9863,7 @@ ${p}` : p;
|
|
|
9821
9863
|
} catch (err) {
|
|
9822
9864
|
if (attempt === maxRetries) throw err;
|
|
9823
9865
|
const delay = baseDelayMs * Math.pow(2, attempt - 1);
|
|
9824
|
-
|
|
9866
|
+
log36.warn(
|
|
9825
9867
|
{ err, attempt, maxRetries, delayMs: delay, operation: label },
|
|
9826
9868
|
`${label} failed, retrying in ${delay}ms`
|
|
9827
9869
|
);
|
|
@@ -9841,12 +9883,12 @@ ${p}` : p;
|
|
|
9841
9883
|
}),
|
|
9842
9884
|
"setMyCommands"
|
|
9843
9885
|
).catch((err) => {
|
|
9844
|
-
|
|
9886
|
+
log36.warn({ err }, "Failed to register Telegram commands after retries (non-critical)");
|
|
9845
9887
|
});
|
|
9846
9888
|
}
|
|
9847
9889
|
async initTopicDependentFeatures() {
|
|
9848
9890
|
if (this._topicsInitialized) return;
|
|
9849
|
-
|
|
9891
|
+
log36.info(
|
|
9850
9892
|
{ notificationTopicId: this.telegramConfig.notificationTopicId, assistantTopicId: this.telegramConfig.assistantTopicId },
|
|
9851
9893
|
"initTopicDependentFeatures: starting (existing IDs in config)"
|
|
9852
9894
|
);
|
|
@@ -9871,7 +9913,7 @@ ${p}` : p;
|
|
|
9871
9913
|
this.assistantTopicId = topics.assistantTopicId;
|
|
9872
9914
|
this._systemTopicIds.notificationTopicId = topics.notificationTopicId;
|
|
9873
9915
|
this._systemTopicIds.assistantTopicId = topics.assistantTopicId;
|
|
9874
|
-
|
|
9916
|
+
log36.info(
|
|
9875
9917
|
{ notificationTopicId: this.notificationTopicId, assistantTopicId: this.assistantTopicId },
|
|
9876
9918
|
"initTopicDependentFeatures: topics ready"
|
|
9877
9919
|
);
|
|
@@ -9902,7 +9944,7 @@ ${p}` : p;
|
|
|
9902
9944
|
).then((msg) => {
|
|
9903
9945
|
if (msg) this.storeControlMsgId(sessionId, msg.message_id);
|
|
9904
9946
|
}).catch((err) => {
|
|
9905
|
-
|
|
9947
|
+
log36.warn({ err, sessionId }, "Failed to send initial messages for new session");
|
|
9906
9948
|
});
|
|
9907
9949
|
};
|
|
9908
9950
|
this.core.eventBus.on(BusEvent.SESSION_THREAD_READY, this._threadReadyHandler);
|
|
@@ -9911,7 +9953,7 @@ ${p}` : p;
|
|
|
9911
9953
|
});
|
|
9912
9954
|
};
|
|
9913
9955
|
this.core.eventBus.on("session:configChanged", this._configChangedHandler);
|
|
9914
|
-
|
|
9956
|
+
log36.info({ assistantTopicId: this.assistantTopicId }, "initTopicDependentFeatures: sending welcome message");
|
|
9915
9957
|
try {
|
|
9916
9958
|
const config = this.core.configManager.get();
|
|
9917
9959
|
const agents = this.core.agentManager.getAvailableAgents();
|
|
@@ -9933,17 +9975,17 @@ ${p}` : p;
|
|
|
9933
9975
|
this.core.lifecycleManager?.serviceRegistry?.get("menu-registry")
|
|
9934
9976
|
)
|
|
9935
9977
|
});
|
|
9936
|
-
|
|
9978
|
+
log36.info("initTopicDependentFeatures: welcome message sent");
|
|
9937
9979
|
} catch (err) {
|
|
9938
|
-
|
|
9980
|
+
log36.warn({ err }, "Failed to send welcome message");
|
|
9939
9981
|
}
|
|
9940
9982
|
try {
|
|
9941
9983
|
await this.core.assistantManager.getOrSpawn("telegram", String(this.assistantTopicId));
|
|
9942
9984
|
} catch (err) {
|
|
9943
|
-
|
|
9985
|
+
log36.error({ err }, "Failed to spawn assistant");
|
|
9944
9986
|
}
|
|
9945
9987
|
this._topicsInitialized = true;
|
|
9946
|
-
|
|
9988
|
+
log36.info("Telegram adapter fully initialized");
|
|
9947
9989
|
}
|
|
9948
9990
|
startPrerequisiteWatcher(issues) {
|
|
9949
9991
|
const setupMessage = `\u26A0\uFE0F <b>OpenACP needs setup before it can start.</b>
|
|
@@ -9954,7 +9996,7 @@ OpenACP will automatically retry until this is resolved.`;
|
|
|
9954
9996
|
this.bot.api.sendMessage(this.telegramConfig.chatId, setupMessage, {
|
|
9955
9997
|
parse_mode: "HTML"
|
|
9956
9998
|
}).catch((err) => {
|
|
9957
|
-
|
|
9999
|
+
log36.warn({ err }, "Failed to send setup guidance to General topic");
|
|
9958
10000
|
});
|
|
9959
10001
|
const schedule = [5e3, 1e4, 3e4];
|
|
9960
10002
|
let attempt = 1;
|
|
@@ -9967,7 +10009,7 @@ OpenACP will automatically retry until this is resolved.`;
|
|
|
9967
10009
|
);
|
|
9968
10010
|
if (result.ok) {
|
|
9969
10011
|
this._prerequisiteWatcher = null;
|
|
9970
|
-
|
|
10012
|
+
log36.info("Prerequisites met \u2014 completing Telegram adapter initialization");
|
|
9971
10013
|
try {
|
|
9972
10014
|
await this.initTopicDependentFeatures();
|
|
9973
10015
|
await this.bot.api.sendMessage(
|
|
@@ -9976,11 +10018,11 @@ OpenACP will automatically retry until this is resolved.`;
|
|
|
9976
10018
|
{ parse_mode: "HTML" }
|
|
9977
10019
|
);
|
|
9978
10020
|
} catch (err) {
|
|
9979
|
-
|
|
10021
|
+
log36.error({ err }, "Failed to complete initialization after prerequisites met");
|
|
9980
10022
|
}
|
|
9981
10023
|
return;
|
|
9982
10024
|
}
|
|
9983
|
-
|
|
10025
|
+
log36.debug({ issues: result.issues }, "Prerequisites not yet met, retrying");
|
|
9984
10026
|
const delay = schedule[Math.min(attempt, schedule.length - 1)];
|
|
9985
10027
|
attempt++;
|
|
9986
10028
|
this._prerequisiteWatcher = setTimeout(retry, delay);
|
|
@@ -10006,7 +10048,7 @@ OpenACP will automatically retry until this is resolved.`;
|
|
|
10006
10048
|
}
|
|
10007
10049
|
this.sendQueue.clear();
|
|
10008
10050
|
await this.bot.stop();
|
|
10009
|
-
|
|
10051
|
+
log36.info("Telegram bot stopped");
|
|
10010
10052
|
}
|
|
10011
10053
|
// --- CommandRegistry response rendering ---
|
|
10012
10054
|
async renderCommandResponse(response, chatId, topicId) {
|
|
@@ -10130,7 +10172,7 @@ ${lines.join("\n")}`;
|
|
|
10130
10172
|
threadId: String(threadId),
|
|
10131
10173
|
userId: String(ctx.from.id),
|
|
10132
10174
|
text: forwardText
|
|
10133
|
-
}).catch((err) =>
|
|
10175
|
+
}).catch((err) => log36.error({ err }, "handleMessage error"));
|
|
10134
10176
|
});
|
|
10135
10177
|
this.bot.on("message:photo", async (ctx) => {
|
|
10136
10178
|
const threadId = ctx.message.message_thread_id;
|
|
@@ -10219,7 +10261,7 @@ ${lines.join("\n")}`;
|
|
|
10219
10261
|
if (session.archiving) return;
|
|
10220
10262
|
const threadId = Number(session.threadId);
|
|
10221
10263
|
if (!threadId || isNaN(threadId)) {
|
|
10222
|
-
|
|
10264
|
+
log36.warn(
|
|
10223
10265
|
{ sessionId, threadId: session.threadId },
|
|
10224
10266
|
"Session has no valid threadId, skipping message"
|
|
10225
10267
|
);
|
|
@@ -10235,7 +10277,7 @@ ${lines.join("\n")}`;
|
|
|
10235
10277
|
this._sessionThreadIds.delete(sessionId);
|
|
10236
10278
|
}
|
|
10237
10279
|
}).catch((err) => {
|
|
10238
|
-
|
|
10280
|
+
log36.warn({ err, sessionId }, "Dispatch queue error");
|
|
10239
10281
|
});
|
|
10240
10282
|
this._dispatchQueues.set(sessionId, next);
|
|
10241
10283
|
await next;
|
|
@@ -10357,7 +10399,7 @@ ${lines.join("\n")}`;
|
|
|
10357
10399
|
);
|
|
10358
10400
|
usageMsgId = result?.message_id;
|
|
10359
10401
|
} catch (err) {
|
|
10360
|
-
|
|
10402
|
+
log36.warn({ err, sessionId }, "Failed to send usage message");
|
|
10361
10403
|
}
|
|
10362
10404
|
if (this.notificationTopicId && sessionId !== this.core.assistantManager?.get("telegram")?.id) {
|
|
10363
10405
|
const sess = this.core.sessionManager.getSession(sessionId);
|
|
@@ -10385,7 +10427,7 @@ Task completed.
|
|
|
10385
10427
|
if (!content.attachment) return;
|
|
10386
10428
|
const { attachment } = content;
|
|
10387
10429
|
if (attachment.size > 50 * 1024 * 1024) {
|
|
10388
|
-
|
|
10430
|
+
log36.warn(
|
|
10389
10431
|
{
|
|
10390
10432
|
sessionId,
|
|
10391
10433
|
fileName: attachment.fileName,
|
|
@@ -10429,7 +10471,7 @@ Task completed.
|
|
|
10429
10471
|
);
|
|
10430
10472
|
}
|
|
10431
10473
|
} catch (err) {
|
|
10432
|
-
|
|
10474
|
+
log36.error(
|
|
10433
10475
|
{ err, sessionId, fileName: attachment.fileName },
|
|
10434
10476
|
"Failed to send attachment"
|
|
10435
10477
|
);
|
|
@@ -10528,7 +10570,7 @@ Task completed.
|
|
|
10528
10570
|
}
|
|
10529
10571
|
async sendPermissionRequest(sessionId, request) {
|
|
10530
10572
|
this.getTracer(sessionId)?.log("telegram", { action: "permission:send", sessionId, requestId: request.id, description: request.description });
|
|
10531
|
-
|
|
10573
|
+
log36.info({ sessionId, requestId: request.id }, "Permission request sent");
|
|
10532
10574
|
const session = this.core.sessionManager.getSession(sessionId);
|
|
10533
10575
|
if (!session) return;
|
|
10534
10576
|
await this.sendQueue.enqueue(
|
|
@@ -10538,7 +10580,7 @@ Task completed.
|
|
|
10538
10580
|
async sendNotification(notification) {
|
|
10539
10581
|
this.getTracer(notification.sessionId)?.log("telegram", { action: "notification:send", sessionId: notification.sessionId, type: notification.type });
|
|
10540
10582
|
if (notification.sessionId === this.core.assistantManager?.get("telegram")?.id) return;
|
|
10541
|
-
|
|
10583
|
+
log36.info(
|
|
10542
10584
|
{ sessionId: notification.sessionId, type: notification.type },
|
|
10543
10585
|
"Notification sent"
|
|
10544
10586
|
);
|
|
@@ -10577,7 +10619,7 @@ Task completed.
|
|
|
10577
10619
|
}
|
|
10578
10620
|
async createSessionThread(sessionId, name) {
|
|
10579
10621
|
this.getTracer(sessionId)?.log("telegram", { action: "thread:create", sessionId, name });
|
|
10580
|
-
|
|
10622
|
+
log36.info({ sessionId, name }, "Session topic created");
|
|
10581
10623
|
return String(
|
|
10582
10624
|
await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
|
|
10583
10625
|
);
|
|
@@ -10588,7 +10630,7 @@ Task completed.
|
|
|
10588
10630
|
if (!session) return;
|
|
10589
10631
|
const threadId = Number(session.threadId);
|
|
10590
10632
|
if (!threadId) {
|
|
10591
|
-
|
|
10633
|
+
log36.debug({ sessionId, newName }, "Cannot rename thread \u2014 threadId not set yet");
|
|
10592
10634
|
return;
|
|
10593
10635
|
}
|
|
10594
10636
|
await renameSessionTopic(
|
|
@@ -10607,7 +10649,7 @@ Task completed.
|
|
|
10607
10649
|
try {
|
|
10608
10650
|
await this.bot.api.deleteForumTopic(this.telegramConfig.chatId, topicId);
|
|
10609
10651
|
} catch (err) {
|
|
10610
|
-
|
|
10652
|
+
log36.warn(
|
|
10611
10653
|
{ err, sessionId, topicId },
|
|
10612
10654
|
"Failed to delete forum topic (may already be deleted)"
|
|
10613
10655
|
);
|
|
@@ -10651,7 +10693,7 @@ Task completed.
|
|
|
10651
10693
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
10652
10694
|
return { buffer, filePath: file.file_path };
|
|
10653
10695
|
} catch (err) {
|
|
10654
|
-
|
|
10696
|
+
log36.error({ err }, "Failed to download file from Telegram");
|
|
10655
10697
|
return null;
|
|
10656
10698
|
}
|
|
10657
10699
|
}
|
|
@@ -10672,7 +10714,7 @@ Task completed.
|
|
|
10672
10714
|
try {
|
|
10673
10715
|
buffer = await this.fileService.convertOggToWav(buffer);
|
|
10674
10716
|
} catch (err) {
|
|
10675
|
-
|
|
10717
|
+
log36.warn({ err }, "OGG\u2192WAV conversion failed, saving original OGG");
|
|
10676
10718
|
fileName = "voice.ogg";
|
|
10677
10719
|
mimeType = "audio/ogg";
|
|
10678
10720
|
originalFilePath = void 0;
|
|
@@ -10704,7 +10746,7 @@ Task completed.
|
|
|
10704
10746
|
userId: String(userId),
|
|
10705
10747
|
text: text3,
|
|
10706
10748
|
attachments: [att]
|
|
10707
|
-
}).catch((err) =>
|
|
10749
|
+
}).catch((err) => log36.error({ err }, "handleMessage error"));
|
|
10708
10750
|
}
|
|
10709
10751
|
async cleanupSkillCommands(sessionId) {
|
|
10710
10752
|
this._pendingSkillCommands.delete(sessionId);
|
|
@@ -10778,15 +10820,15 @@ var ChannelAdapter = class {
|
|
|
10778
10820
|
function nodeToWebWritable(nodeStream) {
|
|
10779
10821
|
return new WritableStream({
|
|
10780
10822
|
write(chunk) {
|
|
10781
|
-
return new Promise((
|
|
10823
|
+
return new Promise((resolve7, reject) => {
|
|
10782
10824
|
const ok2 = nodeStream.write(chunk);
|
|
10783
10825
|
if (ok2) {
|
|
10784
|
-
|
|
10826
|
+
resolve7();
|
|
10785
10827
|
return;
|
|
10786
10828
|
}
|
|
10787
10829
|
const onDrain = () => {
|
|
10788
10830
|
nodeStream.removeListener("error", onError);
|
|
10789
|
-
|
|
10831
|
+
resolve7();
|
|
10790
10832
|
};
|
|
10791
10833
|
const onError = (err) => {
|
|
10792
10834
|
nodeStream.removeListener("drain", onDrain);
|
|
@@ -10840,13 +10882,13 @@ init_config();
|
|
|
10840
10882
|
// src/core/agents/agent-instance.ts
|
|
10841
10883
|
import { spawn as spawn2, execFileSync } from "child_process";
|
|
10842
10884
|
import { Transform } from "stream";
|
|
10843
|
-
import
|
|
10844
|
-
import
|
|
10885
|
+
import fs7 from "fs";
|
|
10886
|
+
import path6 from "path";
|
|
10845
10887
|
import { ClientSideConnection, ndJsonStream } from "@agentclientprotocol/sdk";
|
|
10846
10888
|
|
|
10847
10889
|
// src/core/security/path-guard.ts
|
|
10848
|
-
import
|
|
10849
|
-
import
|
|
10890
|
+
import fs4 from "fs";
|
|
10891
|
+
import path4 from "path";
|
|
10850
10892
|
import ignore from "ignore";
|
|
10851
10893
|
var DEFAULT_DENY_PATTERNS = [
|
|
10852
10894
|
".env",
|
|
@@ -10866,15 +10908,15 @@ var PathGuard = class {
|
|
|
10866
10908
|
ig;
|
|
10867
10909
|
constructor(options) {
|
|
10868
10910
|
try {
|
|
10869
|
-
this.cwd =
|
|
10911
|
+
this.cwd = fs4.realpathSync(path4.resolve(options.cwd));
|
|
10870
10912
|
} catch {
|
|
10871
|
-
this.cwd =
|
|
10913
|
+
this.cwd = path4.resolve(options.cwd);
|
|
10872
10914
|
}
|
|
10873
10915
|
this.allowedPaths = options.allowedPaths.map((p) => {
|
|
10874
10916
|
try {
|
|
10875
|
-
return
|
|
10917
|
+
return fs4.realpathSync(path4.resolve(p));
|
|
10876
10918
|
} catch {
|
|
10877
|
-
return
|
|
10919
|
+
return path4.resolve(p);
|
|
10878
10920
|
}
|
|
10879
10921
|
});
|
|
10880
10922
|
this.ig = ignore();
|
|
@@ -10884,19 +10926,19 @@ var PathGuard = class {
|
|
|
10884
10926
|
}
|
|
10885
10927
|
}
|
|
10886
10928
|
validatePath(targetPath, operation) {
|
|
10887
|
-
const resolved =
|
|
10929
|
+
const resolved = path4.resolve(targetPath);
|
|
10888
10930
|
let realPath;
|
|
10889
10931
|
try {
|
|
10890
|
-
realPath =
|
|
10932
|
+
realPath = fs4.realpathSync(resolved);
|
|
10891
10933
|
} catch {
|
|
10892
10934
|
realPath = resolved;
|
|
10893
10935
|
}
|
|
10894
|
-
if (operation === "write" &&
|
|
10936
|
+
if (operation === "write" && path4.basename(realPath) === ".openacpignore") {
|
|
10895
10937
|
return { allowed: false, reason: "Cannot write to .openacpignore" };
|
|
10896
10938
|
}
|
|
10897
|
-
const isWithinCwd = realPath === this.cwd || realPath.startsWith(this.cwd +
|
|
10939
|
+
const isWithinCwd = realPath === this.cwd || realPath.startsWith(this.cwd + path4.sep);
|
|
10898
10940
|
const isWithinAllowed = this.allowedPaths.some(
|
|
10899
|
-
(ap) => realPath === ap || realPath.startsWith(ap +
|
|
10941
|
+
(ap) => realPath === ap || realPath.startsWith(ap + path4.sep)
|
|
10900
10942
|
);
|
|
10901
10943
|
if (!isWithinCwd && !isWithinAllowed) {
|
|
10902
10944
|
return {
|
|
@@ -10905,7 +10947,7 @@ var PathGuard = class {
|
|
|
10905
10947
|
};
|
|
10906
10948
|
}
|
|
10907
10949
|
if (isWithinCwd && !isWithinAllowed) {
|
|
10908
|
-
const relativePath =
|
|
10950
|
+
const relativePath = path4.relative(this.cwd, realPath);
|
|
10909
10951
|
if (relativePath === ".openacpignore") {
|
|
10910
10952
|
return { allowed: true, reason: "" };
|
|
10911
10953
|
}
|
|
@@ -10920,15 +10962,15 @@ var PathGuard = class {
|
|
|
10920
10962
|
}
|
|
10921
10963
|
addAllowedPath(p) {
|
|
10922
10964
|
try {
|
|
10923
|
-
this.allowedPaths.push(
|
|
10965
|
+
this.allowedPaths.push(fs4.realpathSync(path4.resolve(p)));
|
|
10924
10966
|
} catch {
|
|
10925
|
-
this.allowedPaths.push(
|
|
10967
|
+
this.allowedPaths.push(path4.resolve(p));
|
|
10926
10968
|
}
|
|
10927
10969
|
}
|
|
10928
10970
|
static loadIgnoreFile(cwd) {
|
|
10929
|
-
const ignorePath =
|
|
10971
|
+
const ignorePath = path4.join(cwd, ".openacpignore");
|
|
10930
10972
|
try {
|
|
10931
|
-
const content =
|
|
10973
|
+
const content = fs4.readFileSync(ignorePath, "utf-8");
|
|
10932
10974
|
return content.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
10933
10975
|
} catch {
|
|
10934
10976
|
return [];
|
|
@@ -11222,12 +11264,12 @@ var TerminalManager = class {
|
|
|
11222
11264
|
signal: state.exitStatus.signal
|
|
11223
11265
|
};
|
|
11224
11266
|
}
|
|
11225
|
-
return new Promise((
|
|
11267
|
+
return new Promise((resolve7) => {
|
|
11226
11268
|
state.process.on("exit", (code, signal) => {
|
|
11227
|
-
|
|
11269
|
+
resolve7({ exitCode: code, signal });
|
|
11228
11270
|
});
|
|
11229
11271
|
if (state.exitStatus !== null) {
|
|
11230
|
-
|
|
11272
|
+
resolve7({
|
|
11231
11273
|
exitCode: state.exitStatus.exitCode,
|
|
11232
11274
|
signal: state.exitStatus.signal
|
|
11233
11275
|
});
|
|
@@ -11269,24 +11311,24 @@ var McpManager = class {
|
|
|
11269
11311
|
};
|
|
11270
11312
|
|
|
11271
11313
|
// src/core/utils/debug-tracer.ts
|
|
11272
|
-
import
|
|
11273
|
-
import
|
|
11314
|
+
import fs6 from "fs";
|
|
11315
|
+
import path5 from "path";
|
|
11274
11316
|
var DEBUG_ENABLED = process.env.OPENACP_DEBUG === "true" || process.env.OPENACP_DEBUG === "1";
|
|
11275
11317
|
var DebugTracer = class {
|
|
11276
11318
|
constructor(sessionId, workingDirectory) {
|
|
11277
11319
|
this.sessionId = sessionId;
|
|
11278
11320
|
this.workingDirectory = workingDirectory;
|
|
11279
|
-
this.logDir =
|
|
11321
|
+
this.logDir = path5.join(workingDirectory, ".log");
|
|
11280
11322
|
}
|
|
11281
11323
|
dirCreated = false;
|
|
11282
11324
|
logDir;
|
|
11283
11325
|
log(layer, data) {
|
|
11284
11326
|
try {
|
|
11285
11327
|
if (!this.dirCreated) {
|
|
11286
|
-
|
|
11328
|
+
fs6.mkdirSync(this.logDir, { recursive: true });
|
|
11287
11329
|
this.dirCreated = true;
|
|
11288
11330
|
}
|
|
11289
|
-
const filePath =
|
|
11331
|
+
const filePath = path5.join(this.logDir, `${this.sessionId}_${layer}.jsonl`);
|
|
11290
11332
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
11291
11333
|
const line = JSON.stringify({ ts: Date.now(), ...data }, (_key, value) => {
|
|
11292
11334
|
if (typeof value === "object" && value !== null) {
|
|
@@ -11295,7 +11337,7 @@ var DebugTracer = class {
|
|
|
11295
11337
|
}
|
|
11296
11338
|
return value;
|
|
11297
11339
|
}) + "\n";
|
|
11298
|
-
|
|
11340
|
+
fs6.appendFileSync(filePath, line);
|
|
11299
11341
|
} catch {
|
|
11300
11342
|
}
|
|
11301
11343
|
}
|
|
@@ -11314,11 +11356,11 @@ init_events();
|
|
|
11314
11356
|
var log4 = createChildLogger({ module: "agent-instance" });
|
|
11315
11357
|
function findPackageRoot(startDir) {
|
|
11316
11358
|
let dir = startDir;
|
|
11317
|
-
while (dir !==
|
|
11318
|
-
if (
|
|
11359
|
+
while (dir !== path6.dirname(dir)) {
|
|
11360
|
+
if (fs7.existsSync(path6.join(dir, "package.json"))) {
|
|
11319
11361
|
return dir;
|
|
11320
11362
|
}
|
|
11321
|
-
dir =
|
|
11363
|
+
dir = path6.dirname(dir);
|
|
11322
11364
|
}
|
|
11323
11365
|
return startDir;
|
|
11324
11366
|
}
|
|
@@ -11330,26 +11372,26 @@ function resolveAgentCommand(cmd) {
|
|
|
11330
11372
|
}
|
|
11331
11373
|
for (const root of searchRoots) {
|
|
11332
11374
|
const packageDirs = [
|
|
11333
|
-
|
|
11334
|
-
|
|
11375
|
+
path6.resolve(root, "node_modules", "@zed-industries", cmd, "dist", "index.js"),
|
|
11376
|
+
path6.resolve(root, "node_modules", cmd, "dist", "index.js")
|
|
11335
11377
|
];
|
|
11336
11378
|
for (const jsPath of packageDirs) {
|
|
11337
|
-
if (
|
|
11379
|
+
if (fs7.existsSync(jsPath)) {
|
|
11338
11380
|
return { command: process.execPath, args: [jsPath] };
|
|
11339
11381
|
}
|
|
11340
11382
|
}
|
|
11341
11383
|
}
|
|
11342
11384
|
for (const root of searchRoots) {
|
|
11343
|
-
const localBin =
|
|
11344
|
-
if (
|
|
11345
|
-
const content =
|
|
11385
|
+
const localBin = path6.resolve(root, "node_modules", ".bin", cmd);
|
|
11386
|
+
if (fs7.existsSync(localBin)) {
|
|
11387
|
+
const content = fs7.readFileSync(localBin, "utf-8");
|
|
11346
11388
|
if (content.startsWith("#!/usr/bin/env node")) {
|
|
11347
11389
|
return { command: process.execPath, args: [localBin] };
|
|
11348
11390
|
}
|
|
11349
11391
|
const match = content.match(/"([^"]+\.js)"/);
|
|
11350
11392
|
if (match) {
|
|
11351
|
-
const target =
|
|
11352
|
-
if (
|
|
11393
|
+
const target = path6.resolve(path6.dirname(localBin), match[1]);
|
|
11394
|
+
if (fs7.existsSync(target)) {
|
|
11353
11395
|
return { command: process.execPath, args: [target] };
|
|
11354
11396
|
}
|
|
11355
11397
|
}
|
|
@@ -11359,7 +11401,7 @@ function resolveAgentCommand(cmd) {
|
|
|
11359
11401
|
const fullPath = execFileSync("which", [cmd], { encoding: "utf-8" }).trim();
|
|
11360
11402
|
if (fullPath) {
|
|
11361
11403
|
try {
|
|
11362
|
-
const content =
|
|
11404
|
+
const content = fs7.readFileSync(fullPath, "utf-8");
|
|
11363
11405
|
if (content.startsWith("#!/usr/bin/env node")) {
|
|
11364
11406
|
return { command: process.execPath, args: [fullPath] };
|
|
11365
11407
|
}
|
|
@@ -11378,16 +11420,16 @@ function resolveAgentCommand(cmd) {
|
|
|
11378
11420
|
candidates.push(dir);
|
|
11379
11421
|
}
|
|
11380
11422
|
};
|
|
11381
|
-
addCandidate(
|
|
11423
|
+
addCandidate(path6.dirname(process.execPath));
|
|
11382
11424
|
try {
|
|
11383
|
-
addCandidate(
|
|
11425
|
+
addCandidate(path6.dirname(fs7.realpathSync(process.execPath)));
|
|
11384
11426
|
} catch {
|
|
11385
11427
|
}
|
|
11386
11428
|
addCandidate("/opt/homebrew/bin");
|
|
11387
11429
|
addCandidate("/usr/local/bin");
|
|
11388
11430
|
for (const dir of candidates) {
|
|
11389
|
-
const candidate =
|
|
11390
|
-
if (
|
|
11431
|
+
const candidate = path6.join(dir, cmd);
|
|
11432
|
+
if (fs7.existsSync(candidate)) {
|
|
11391
11433
|
log4.info({ cmd, resolved: candidate }, "Resolved package runner from fallback search");
|
|
11392
11434
|
return { command: candidate, args: [] };
|
|
11393
11435
|
}
|
|
@@ -11450,7 +11492,7 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
|
|
|
11450
11492
|
env: filterEnv(process.env, agentDef.env)
|
|
11451
11493
|
}
|
|
11452
11494
|
);
|
|
11453
|
-
await new Promise((
|
|
11495
|
+
await new Promise((resolve7, reject) => {
|
|
11454
11496
|
instance.child.on("error", (err) => {
|
|
11455
11497
|
reject(
|
|
11456
11498
|
new Error(
|
|
@@ -11458,7 +11500,7 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
|
|
|
11458
11500
|
)
|
|
11459
11501
|
);
|
|
11460
11502
|
});
|
|
11461
|
-
instance.child.on("spawn", () =>
|
|
11503
|
+
instance.child.on("spawn", () => resolve7());
|
|
11462
11504
|
});
|
|
11463
11505
|
instance.stderrCapture = new StderrCapture(50);
|
|
11464
11506
|
instance.child.stderr.on("data", (chunk) => {
|
|
@@ -11554,10 +11596,11 @@ ${stderr}`
|
|
|
11554
11596
|
allowedPaths
|
|
11555
11597
|
);
|
|
11556
11598
|
const resolvedMcp = _AgentInstance.mcpManager.resolve(mcpServers);
|
|
11557
|
-
const response = await
|
|
11558
|
-
cwd: workingDirectory,
|
|
11559
|
-
|
|
11560
|
-
|
|
11599
|
+
const response = await withAgentTimeout(
|
|
11600
|
+
instance.connection.newSession({ cwd: workingDirectory, mcpServers: resolvedMcp }),
|
|
11601
|
+
agentDef.name,
|
|
11602
|
+
"newSession"
|
|
11603
|
+
);
|
|
11561
11604
|
log4.info(response, "newSession response");
|
|
11562
11605
|
instance.sessionId = response.sessionId;
|
|
11563
11606
|
instance.initialSessionResponse = response;
|
|
@@ -11585,11 +11628,11 @@ ${stderr}`
|
|
|
11585
11628
|
const resolvedMcp = _AgentInstance.mcpManager.resolve(mcpServers);
|
|
11586
11629
|
try {
|
|
11587
11630
|
if (instance.agentCapabilities?.loadSession) {
|
|
11588
|
-
const response = await
|
|
11589
|
-
sessionId: agentSessionId,
|
|
11590
|
-
|
|
11591
|
-
|
|
11592
|
-
|
|
11631
|
+
const response = await withAgentTimeout(
|
|
11632
|
+
instance.connection.loadSession({ sessionId: agentSessionId, cwd: workingDirectory, mcpServers: resolvedMcp }),
|
|
11633
|
+
agentDef.name,
|
|
11634
|
+
"loadSession"
|
|
11635
|
+
);
|
|
11593
11636
|
instance.sessionId = agentSessionId;
|
|
11594
11637
|
instance.initialSessionResponse = response;
|
|
11595
11638
|
instance.debugTracer = createDebugTracer(agentSessionId, workingDirectory);
|
|
@@ -11602,10 +11645,11 @@ ${stderr}`
|
|
|
11602
11645
|
"Agent load complete"
|
|
11603
11646
|
);
|
|
11604
11647
|
} else {
|
|
11605
|
-
const response = await
|
|
11606
|
-
sessionId: agentSessionId,
|
|
11607
|
-
|
|
11608
|
-
|
|
11648
|
+
const response = await withAgentTimeout(
|
|
11649
|
+
instance.connection.unstable_resumeSession({ sessionId: agentSessionId, cwd: workingDirectory }),
|
|
11650
|
+
agentDef.name,
|
|
11651
|
+
"resumeSession"
|
|
11652
|
+
);
|
|
11609
11653
|
instance.sessionId = response.sessionId;
|
|
11610
11654
|
instance.initialSessionResponse = response;
|
|
11611
11655
|
instance.debugTracer = createDebugTracer(response.sessionId, workingDirectory);
|
|
@@ -11623,10 +11667,11 @@ ${stderr}`
|
|
|
11623
11667
|
{ err, agentSessionId },
|
|
11624
11668
|
"Resume failed, falling back to new session"
|
|
11625
11669
|
);
|
|
11626
|
-
const response = await
|
|
11627
|
-
cwd: workingDirectory,
|
|
11628
|
-
|
|
11629
|
-
|
|
11670
|
+
const response = await withAgentTimeout(
|
|
11671
|
+
instance.connection.newSession({ cwd: workingDirectory, mcpServers: resolvedMcp }),
|
|
11672
|
+
agentDef.name,
|
|
11673
|
+
"newSession (fallback)"
|
|
11674
|
+
);
|
|
11630
11675
|
instance.sessionId = response.sessionId;
|
|
11631
11676
|
instance.initialSessionResponse = response;
|
|
11632
11677
|
instance.debugTracer = createDebugTracer(response.sessionId, workingDirectory);
|
|
@@ -11818,8 +11863,8 @@ ${stderr}`
|
|
|
11818
11863
|
writePath = result.path;
|
|
11819
11864
|
writeContent = result.content;
|
|
11820
11865
|
}
|
|
11821
|
-
await
|
|
11822
|
-
await
|
|
11866
|
+
await fs7.promises.mkdir(path6.dirname(writePath), { recursive: true });
|
|
11867
|
+
await fs7.promises.writeFile(writePath, writeContent, "utf-8");
|
|
11823
11868
|
return {};
|
|
11824
11869
|
},
|
|
11825
11870
|
// ── Terminal operations (delegated to TerminalManager) ─────────────
|
|
@@ -11927,7 +11972,7 @@ ${skipNote}`;
|
|
|
11927
11972
|
[Attachment access denied: ${attCheck.reason}]`;
|
|
11928
11973
|
continue;
|
|
11929
11974
|
}
|
|
11930
|
-
const data = await
|
|
11975
|
+
const data = await fs7.promises.readFile(att.filePath);
|
|
11931
11976
|
contentBlocks.push({ type: "image", data: data.toString("base64"), mimeType: att.mimeType });
|
|
11932
11977
|
} else if (att.type === "audio" && capabilities.audio) {
|
|
11933
11978
|
const attCheck = this.pathGuard.validatePath(att.filePath, "read");
|
|
@@ -11937,7 +11982,7 @@ ${skipNote}`;
|
|
|
11937
11982
|
[Attachment access denied: ${attCheck.reason}]`;
|
|
11938
11983
|
continue;
|
|
11939
11984
|
}
|
|
11940
|
-
const data = await
|
|
11985
|
+
const data = await fs7.promises.readFile(att.filePath);
|
|
11941
11986
|
contentBlocks.push({ type: "audio", data: data.toString("base64"), mimeType: att.mimeType });
|
|
11942
11987
|
} else {
|
|
11943
11988
|
if (att.type === "image" || att.type === "audio") {
|
|
@@ -11964,15 +12009,15 @@ ${skipNote}`;
|
|
|
11964
12009
|
this._destroying = true;
|
|
11965
12010
|
this.terminalManager.destroyAll();
|
|
11966
12011
|
if (this.child.exitCode !== null) return;
|
|
11967
|
-
await new Promise((
|
|
12012
|
+
await new Promise((resolve7) => {
|
|
11968
12013
|
this.child.on("exit", () => {
|
|
11969
12014
|
clearTimeout(forceKillTimer);
|
|
11970
|
-
|
|
12015
|
+
resolve7();
|
|
11971
12016
|
});
|
|
11972
12017
|
this.child.kill("SIGTERM");
|
|
11973
12018
|
const forceKillTimer = setTimeout(() => {
|
|
11974
12019
|
if (this.child.exitCode === null) this.child.kill("SIGKILL");
|
|
11975
|
-
|
|
12020
|
+
resolve7();
|
|
11976
12021
|
}, 1e4);
|
|
11977
12022
|
if (typeof forceKillTimer === "object" && forceKillTimer !== null && "unref" in forceKillTimer) {
|
|
11978
12023
|
forceKillTimer.unref();
|
|
@@ -11980,6 +12025,26 @@ ${skipNote}`;
|
|
|
11980
12025
|
});
|
|
11981
12026
|
}
|
|
11982
12027
|
};
|
|
12028
|
+
var AGENT_INIT_TIMEOUT_MS = 3e4;
|
|
12029
|
+
function withAgentTimeout(promise, agentName, op) {
|
|
12030
|
+
return new Promise((resolve7, reject) => {
|
|
12031
|
+
const timer = setTimeout(() => {
|
|
12032
|
+
reject(new Error(
|
|
12033
|
+
`Agent "${agentName}" did not respond to ${op} within ${AGENT_INIT_TIMEOUT_MS / 1e3}s. The agent process may have hung during initialization.`
|
|
12034
|
+
));
|
|
12035
|
+
}, AGENT_INIT_TIMEOUT_MS);
|
|
12036
|
+
promise.then(
|
|
12037
|
+
(val) => {
|
|
12038
|
+
clearTimeout(timer);
|
|
12039
|
+
resolve7(val);
|
|
12040
|
+
},
|
|
12041
|
+
(err) => {
|
|
12042
|
+
clearTimeout(timer);
|
|
12043
|
+
reject(err);
|
|
12044
|
+
}
|
|
12045
|
+
);
|
|
12046
|
+
});
|
|
12047
|
+
}
|
|
11983
12048
|
|
|
11984
12049
|
// src/core/agents/agent-manager.ts
|
|
11985
12050
|
var AgentManager = class {
|
|
@@ -12026,8 +12091,8 @@ var PromptQueue = class {
|
|
|
12026
12091
|
processorSettled = null;
|
|
12027
12092
|
async enqueue(text3, attachments, routing, turnId) {
|
|
12028
12093
|
if (this.processing) {
|
|
12029
|
-
return new Promise((
|
|
12030
|
-
this.queue.push({ text: text3, attachments, routing, turnId, resolve:
|
|
12094
|
+
return new Promise((resolve7) => {
|
|
12095
|
+
this.queue.push({ text: text3, attachments, routing, turnId, resolve: resolve7 });
|
|
12031
12096
|
});
|
|
12032
12097
|
}
|
|
12033
12098
|
await this.process(text3, attachments, routing, turnId);
|
|
@@ -12101,8 +12166,8 @@ var PermissionGate = class {
|
|
|
12101
12166
|
this.request = request;
|
|
12102
12167
|
this.settled = false;
|
|
12103
12168
|
this.clearTimeout();
|
|
12104
|
-
return new Promise((
|
|
12105
|
-
this.resolveFn =
|
|
12169
|
+
return new Promise((resolve7, reject) => {
|
|
12170
|
+
this.resolveFn = resolve7;
|
|
12106
12171
|
this.rejectFn = reject;
|
|
12107
12172
|
this.timeoutTimer = setTimeout(() => {
|
|
12108
12173
|
this.reject("Permission request timed out (no response received)");
|
|
@@ -12151,7 +12216,7 @@ var PermissionGate = class {
|
|
|
12151
12216
|
|
|
12152
12217
|
// src/core/sessions/session.ts
|
|
12153
12218
|
init_log();
|
|
12154
|
-
import * as
|
|
12219
|
+
import * as fs8 from "fs";
|
|
12155
12220
|
|
|
12156
12221
|
// src/core/sessions/turn-context.ts
|
|
12157
12222
|
import { nanoid } from "nanoid";
|
|
@@ -12477,7 +12542,7 @@ ${text3}`;
|
|
|
12477
12542
|
try {
|
|
12478
12543
|
const audioPath = att.originalFilePath || att.filePath;
|
|
12479
12544
|
const audioMime = att.originalFilePath ? "audio/ogg" : att.mimeType;
|
|
12480
|
-
const audioBuffer = await
|
|
12545
|
+
const audioBuffer = await fs8.promises.readFile(audioPath);
|
|
12481
12546
|
const result = await this.speechService.transcribe(audioBuffer, audioMime);
|
|
12482
12547
|
this.log.info({ provider: "stt", duration: result.duration }, "Voice transcribed");
|
|
12483
12548
|
this.emit(SessionEv.AGENT_EVENT, {
|
|
@@ -12708,10 +12773,14 @@ ${result.text}` : result.text;
|
|
|
12708
12773
|
};
|
|
12709
12774
|
|
|
12710
12775
|
// src/core/message-transformer.ts
|
|
12711
|
-
import * as
|
|
12776
|
+
import * as path7 from "path";
|
|
12712
12777
|
|
|
12713
12778
|
// src/core/utils/extract-file-info.ts
|
|
12714
|
-
|
|
12779
|
+
init_apply_patch_detection();
|
|
12780
|
+
function extractFileInfo(name, kind, content, rawInput, meta, rawOutput) {
|
|
12781
|
+
if (isApplyPatchOtherTool(kind, name, rawInput)) {
|
|
12782
|
+
return parseApplyPatchRawOutput(rawOutput);
|
|
12783
|
+
}
|
|
12715
12784
|
if (kind && !["read", "edit", "write"].includes(kind)) return null;
|
|
12716
12785
|
let info = null;
|
|
12717
12786
|
if (meta) {
|
|
@@ -12766,6 +12835,37 @@ function extractFileInfo(name, kind, content, rawInput, meta) {
|
|
|
12766
12835
|
if (!info.filePath || !info.content) return null;
|
|
12767
12836
|
return info;
|
|
12768
12837
|
}
|
|
12838
|
+
function parseApplyPatchRawOutput(rawOutput) {
|
|
12839
|
+
if (!rawOutput || typeof rawOutput !== "object" || Array.isArray(rawOutput)) return null;
|
|
12840
|
+
const output = rawOutput;
|
|
12841
|
+
const metadata = output.metadata;
|
|
12842
|
+
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) return null;
|
|
12843
|
+
const files = metadata.files;
|
|
12844
|
+
if (!Array.isArray(files)) return null;
|
|
12845
|
+
const sortedFiles = [...files].sort((a, b) => getApplyPatchFileScore(b) - getApplyPatchFileScore(a));
|
|
12846
|
+
for (const file of sortedFiles) {
|
|
12847
|
+
if (!file || typeof file !== "object" || Array.isArray(file)) continue;
|
|
12848
|
+
const f = file;
|
|
12849
|
+
const filePath = typeof f.filePath === "string" ? f.filePath : typeof f.relativePath === "string" ? f.relativePath : null;
|
|
12850
|
+
if (!filePath) continue;
|
|
12851
|
+
const after = typeof f.after === "string" ? f.after : null;
|
|
12852
|
+
if (!after) continue;
|
|
12853
|
+
const before = typeof f.before === "string" ? f.before : void 0;
|
|
12854
|
+
return {
|
|
12855
|
+
filePath,
|
|
12856
|
+
content: after,
|
|
12857
|
+
oldContent: before
|
|
12858
|
+
};
|
|
12859
|
+
}
|
|
12860
|
+
return null;
|
|
12861
|
+
}
|
|
12862
|
+
function getApplyPatchFileScore(file) {
|
|
12863
|
+
if (!file || typeof file !== "object" || Array.isArray(file)) return 0;
|
|
12864
|
+
const f = file;
|
|
12865
|
+
const additions = typeof f.additions === "number" && Number.isFinite(f.additions) && f.additions >= 0 ? f.additions : 0;
|
|
12866
|
+
const deletions = typeof f.deletions === "number" && Number.isFinite(f.deletions) && f.deletions >= 0 ? f.deletions : 0;
|
|
12867
|
+
return additions + deletions;
|
|
12868
|
+
}
|
|
12769
12869
|
function resolveToolResponse(meta) {
|
|
12770
12870
|
const claudeCode = meta.claudeCode;
|
|
12771
12871
|
if (claudeCode?.toolResponse && typeof claudeCode.toolResponse === "object") {
|
|
@@ -12833,6 +12933,7 @@ function parseContent(content) {
|
|
|
12833
12933
|
}
|
|
12834
12934
|
|
|
12835
12935
|
// src/core/message-transformer.ts
|
|
12936
|
+
init_apply_patch_detection();
|
|
12836
12937
|
init_log();
|
|
12837
12938
|
var log5 = createChildLogger({ module: "message-transformer" });
|
|
12838
12939
|
var BINARY_VIEWER_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
@@ -12888,6 +12989,63 @@ function computeLineDiff(oldStr, newStr) {
|
|
|
12888
12989
|
removed: Math.max(0, oldLines.length - prefixLen - suffixLen)
|
|
12889
12990
|
};
|
|
12890
12991
|
}
|
|
12992
|
+
function asRecord2(value) {
|
|
12993
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
12994
|
+
}
|
|
12995
|
+
function asNonNegativeNumber(value) {
|
|
12996
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
12997
|
+
}
|
|
12998
|
+
function extractDiffStatsFromRawOutput(rawOutput) {
|
|
12999
|
+
const output = asRecord2(rawOutput);
|
|
13000
|
+
const metadata = asRecord2(output?.metadata);
|
|
13001
|
+
if (!metadata) return null;
|
|
13002
|
+
const files = Array.isArray(metadata.files) ? metadata.files : null;
|
|
13003
|
+
if (files && files.length > 0) {
|
|
13004
|
+
let added2 = 0;
|
|
13005
|
+
let removed2 = 0;
|
|
13006
|
+
let hasAny = false;
|
|
13007
|
+
for (const file of files) {
|
|
13008
|
+
const fileMeta = asRecord2(file);
|
|
13009
|
+
if (!fileMeta) continue;
|
|
13010
|
+
const fileAdded = asNonNegativeNumber(fileMeta.additions);
|
|
13011
|
+
const fileRemoved = asNonNegativeNumber(fileMeta.deletions);
|
|
13012
|
+
if (fileAdded === null && fileRemoved === null) continue;
|
|
13013
|
+
added2 += fileAdded ?? 0;
|
|
13014
|
+
removed2 += fileRemoved ?? 0;
|
|
13015
|
+
hasAny = true;
|
|
13016
|
+
}
|
|
13017
|
+
if (hasAny && (added2 > 0 || removed2 > 0)) return { added: added2, removed: removed2 };
|
|
13018
|
+
}
|
|
13019
|
+
const added = asNonNegativeNumber(metadata.additions);
|
|
13020
|
+
const removed = asNonNegativeNumber(metadata.deletions);
|
|
13021
|
+
if (added === null && removed === null) return null;
|
|
13022
|
+
if ((added ?? 0) === 0 && (removed ?? 0) === 0) return null;
|
|
13023
|
+
return {
|
|
13024
|
+
added: added ?? 0,
|
|
13025
|
+
removed: removed ?? 0
|
|
13026
|
+
};
|
|
13027
|
+
}
|
|
13028
|
+
function extractDiffStatsFromToolPayload(name, kind, rawInput, rawOutput) {
|
|
13029
|
+
if (kind === "edit" || kind === "write") {
|
|
13030
|
+
const ri = asRecord2(rawInput);
|
|
13031
|
+
if (!ri) return null;
|
|
13032
|
+
const oldStr = typeof ri.old_string === "string" ? ri.old_string : typeof ri.oldText === "string" ? ri.oldText : null;
|
|
13033
|
+
const newStr = typeof ri.new_string === "string" ? ri.new_string : typeof ri.newText === "string" ? ri.newText : typeof ri.content === "string" ? ri.content : null;
|
|
13034
|
+
if (oldStr !== null && newStr !== null) {
|
|
13035
|
+
const stats = computeLineDiff(oldStr, newStr);
|
|
13036
|
+
return stats.added > 0 || stats.removed > 0 ? stats : null;
|
|
13037
|
+
}
|
|
13038
|
+
if (oldStr === null && newStr !== null && kind === "write") {
|
|
13039
|
+
const added = newStr.split("\n").length;
|
|
13040
|
+
return added > 0 ? { added, removed: 0 } : null;
|
|
13041
|
+
}
|
|
13042
|
+
return null;
|
|
13043
|
+
}
|
|
13044
|
+
if (isApplyPatchOtherTool(kind, name, rawInput)) {
|
|
13045
|
+
return extractDiffStatsFromRawOutput(rawOutput);
|
|
13046
|
+
}
|
|
13047
|
+
return null;
|
|
13048
|
+
}
|
|
12891
13049
|
var MessageTransformer = class {
|
|
12892
13050
|
tunnelService;
|
|
12893
13051
|
/** Cache rawInput from tool_call so it's available in tool_update (which often lacks it) */
|
|
@@ -13037,22 +13195,11 @@ var MessageTransformer = class {
|
|
|
13037
13195
|
return input2 !== null && input2 !== void 0 && typeof input2 === "object" && !Array.isArray(input2) && Object.keys(input2).length > 0;
|
|
13038
13196
|
}
|
|
13039
13197
|
enrichWithViewerLinks(event, metadata, sessionContext) {
|
|
13198
|
+
const name = "name" in event ? event.name || "" : "";
|
|
13040
13199
|
const kind = "kind" in event ? event.kind : void 0;
|
|
13041
|
-
if (!metadata.diffStats
|
|
13042
|
-
const
|
|
13043
|
-
if (
|
|
13044
|
-
const oldStr = typeof ri.old_string === "string" ? ri.old_string : typeof ri.oldText === "string" ? ri.oldText : null;
|
|
13045
|
-
const newStr = typeof ri.new_string === "string" ? ri.new_string : typeof ri.newText === "string" ? ri.newText : typeof ri.content === "string" ? ri.content : null;
|
|
13046
|
-
if (oldStr !== null && newStr !== null) {
|
|
13047
|
-
const stats = computeLineDiff(oldStr, newStr);
|
|
13048
|
-
if (stats.added > 0 || stats.removed > 0) {
|
|
13049
|
-
metadata.diffStats = stats;
|
|
13050
|
-
}
|
|
13051
|
-
} else if (oldStr === null && newStr !== null && kind === "write") {
|
|
13052
|
-
const added = newStr.split("\n").length;
|
|
13053
|
-
if (added > 0) metadata.diffStats = { added, removed: 0 };
|
|
13054
|
-
}
|
|
13055
|
-
}
|
|
13200
|
+
if (!metadata.diffStats) {
|
|
13201
|
+
const stats = extractDiffStatsFromToolPayload(name, kind, event.rawInput, event.rawOutput);
|
|
13202
|
+
if (stats) metadata.diffStats = stats;
|
|
13056
13203
|
}
|
|
13057
13204
|
if (!this.tunnelService || !sessionContext) {
|
|
13058
13205
|
log5.debug(
|
|
@@ -13061,7 +13208,6 @@ var MessageTransformer = class {
|
|
|
13061
13208
|
);
|
|
13062
13209
|
return;
|
|
13063
13210
|
}
|
|
13064
|
-
const name = "name" in event ? event.name || "" : "";
|
|
13065
13211
|
log5.debug(
|
|
13066
13212
|
{ name, kind, status: event.status, hasContent: !!event.content, hasRawInput: !!event.rawInput },
|
|
13067
13213
|
"enrichWithViewerLinks: inspecting event"
|
|
@@ -13071,7 +13217,8 @@ var MessageTransformer = class {
|
|
|
13071
13217
|
kind,
|
|
13072
13218
|
event.content,
|
|
13073
13219
|
event.rawInput,
|
|
13074
|
-
event.meta
|
|
13220
|
+
event.meta,
|
|
13221
|
+
event.rawOutput
|
|
13075
13222
|
);
|
|
13076
13223
|
if (!fileInfo) {
|
|
13077
13224
|
log5.debug(
|
|
@@ -13080,7 +13227,7 @@ var MessageTransformer = class {
|
|
|
13080
13227
|
);
|
|
13081
13228
|
return;
|
|
13082
13229
|
}
|
|
13083
|
-
const fileExt =
|
|
13230
|
+
const fileExt = path7.extname(fileInfo.filePath).toLowerCase();
|
|
13084
13231
|
if (BINARY_VIEWER_EXTENSIONS.has(fileExt)) {
|
|
13085
13232
|
log5.debug({ kind, filePath: fileInfo.filePath }, "enrichWithViewerLinks: skipping binary file");
|
|
13086
13233
|
return;
|
|
@@ -13589,12 +13736,12 @@ var SessionBridge = class {
|
|
|
13589
13736
|
break;
|
|
13590
13737
|
case "image_content": {
|
|
13591
13738
|
if (this.deps.fileService) {
|
|
13592
|
-
const
|
|
13739
|
+
const fs35 = this.deps.fileService;
|
|
13593
13740
|
const sid = this.session.id;
|
|
13594
13741
|
const { data, mimeType } = event;
|
|
13595
13742
|
const buffer = Buffer.from(data, "base64");
|
|
13596
|
-
const ext =
|
|
13597
|
-
|
|
13743
|
+
const ext = fs35.extensionFromMime(mimeType);
|
|
13744
|
+
fs35.saveFile(sid, `agent-image${ext}`, buffer, mimeType).then((att) => {
|
|
13598
13745
|
this.sendMessage(sid, {
|
|
13599
13746
|
type: "attachment",
|
|
13600
13747
|
text: "",
|
|
@@ -13606,12 +13753,12 @@ var SessionBridge = class {
|
|
|
13606
13753
|
}
|
|
13607
13754
|
case "audio_content": {
|
|
13608
13755
|
if (this.deps.fileService) {
|
|
13609
|
-
const
|
|
13756
|
+
const fs35 = this.deps.fileService;
|
|
13610
13757
|
const sid = this.session.id;
|
|
13611
13758
|
const { data, mimeType } = event;
|
|
13612
13759
|
const buffer = Buffer.from(data, "base64");
|
|
13613
|
-
const ext =
|
|
13614
|
-
|
|
13760
|
+
const ext = fs35.extensionFromMime(mimeType);
|
|
13761
|
+
fs35.saveFile(sid, `agent-audio${ext}`, buffer, mimeType).then((att) => {
|
|
13615
13762
|
this.sendMessage(sid, {
|
|
13616
13763
|
type: "attachment",
|
|
13617
13764
|
text: "",
|
|
@@ -14163,14 +14310,13 @@ var SessionFactory = class {
|
|
|
14163
14310
|
};
|
|
14164
14311
|
|
|
14165
14312
|
// src/core/core.ts
|
|
14166
|
-
import
|
|
14167
|
-
import os8 from "os";
|
|
14313
|
+
import path14 from "path";
|
|
14168
14314
|
import { nanoid as nanoid3 } from "nanoid";
|
|
14169
14315
|
|
|
14170
14316
|
// src/core/sessions/session-store.ts
|
|
14171
14317
|
init_log();
|
|
14172
|
-
import
|
|
14173
|
-
import
|
|
14318
|
+
import fs9 from "fs";
|
|
14319
|
+
import path8 from "path";
|
|
14174
14320
|
var log8 = createChildLogger({ module: "session-store" });
|
|
14175
14321
|
var DEBOUNCE_MS = 2e3;
|
|
14176
14322
|
var JsonFileSessionStore = class {
|
|
@@ -14252,9 +14398,9 @@ var JsonFileSessionStore = class {
|
|
|
14252
14398
|
version: 1,
|
|
14253
14399
|
sessions: Object.fromEntries(this.records)
|
|
14254
14400
|
};
|
|
14255
|
-
const dir =
|
|
14256
|
-
if (!
|
|
14257
|
-
|
|
14401
|
+
const dir = path8.dirname(this.filePath);
|
|
14402
|
+
if (!fs9.existsSync(dir)) fs9.mkdirSync(dir, { recursive: true });
|
|
14403
|
+
fs9.writeFileSync(this.filePath, JSON.stringify(data, null, 2));
|
|
14258
14404
|
}
|
|
14259
14405
|
destroy() {
|
|
14260
14406
|
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
@@ -14267,10 +14413,10 @@ var JsonFileSessionStore = class {
|
|
|
14267
14413
|
}
|
|
14268
14414
|
}
|
|
14269
14415
|
load() {
|
|
14270
|
-
if (!
|
|
14416
|
+
if (!fs9.existsSync(this.filePath)) return;
|
|
14271
14417
|
try {
|
|
14272
14418
|
const raw = JSON.parse(
|
|
14273
|
-
|
|
14419
|
+
fs9.readFileSync(this.filePath, "utf-8")
|
|
14274
14420
|
);
|
|
14275
14421
|
if (raw.version !== 1) {
|
|
14276
14422
|
log8.warn(
|
|
@@ -14286,7 +14432,7 @@ var JsonFileSessionStore = class {
|
|
|
14286
14432
|
} catch (err) {
|
|
14287
14433
|
log8.error({ err }, "Failed to load session store, backing up corrupt file");
|
|
14288
14434
|
try {
|
|
14289
|
-
|
|
14435
|
+
fs9.renameSync(this.filePath, `${this.filePath}.bak`);
|
|
14290
14436
|
} catch {
|
|
14291
14437
|
}
|
|
14292
14438
|
}
|
|
@@ -14520,92 +14666,13 @@ var AgentSwitchHandler = class {
|
|
|
14520
14666
|
}
|
|
14521
14667
|
};
|
|
14522
14668
|
|
|
14523
|
-
// src/core/agents/agent-catalog.ts
|
|
14524
|
-
import * as fs14 from "fs";
|
|
14525
|
-
import * as path13 from "path";
|
|
14526
|
-
import * as os6 from "os";
|
|
14527
|
-
|
|
14528
|
-
// src/core/agents/agent-store.ts
|
|
14529
|
-
init_log();
|
|
14530
|
-
import * as fs12 from "fs";
|
|
14531
|
-
import * as path11 from "path";
|
|
14532
|
-
import * as os4 from "os";
|
|
14533
|
-
import { z as z2 } from "zod";
|
|
14534
|
-
var log10 = createChildLogger({ module: "agent-store" });
|
|
14535
|
-
var InstalledAgentSchema = z2.object({
|
|
14536
|
-
registryId: z2.string().nullable(),
|
|
14537
|
-
name: z2.string(),
|
|
14538
|
-
version: z2.string(),
|
|
14539
|
-
distribution: z2.enum(["npx", "uvx", "binary", "custom"]),
|
|
14540
|
-
command: z2.string(),
|
|
14541
|
-
args: z2.array(z2.string()).default([]),
|
|
14542
|
-
env: z2.record(z2.string(), z2.string()).default({}),
|
|
14543
|
-
workingDirectory: z2.string().optional(),
|
|
14544
|
-
installedAt: z2.string(),
|
|
14545
|
-
binaryPath: z2.string().nullable().default(null)
|
|
14546
|
-
});
|
|
14547
|
-
var AgentStoreSchema = z2.object({
|
|
14548
|
-
version: z2.number().default(1),
|
|
14549
|
-
installed: z2.record(z2.string(), InstalledAgentSchema).default({})
|
|
14550
|
-
});
|
|
14551
|
-
var AgentStore = class {
|
|
14552
|
-
data = { version: 1, installed: {} };
|
|
14553
|
-
filePath;
|
|
14554
|
-
constructor(filePath) {
|
|
14555
|
-
this.filePath = filePath ?? path11.join(os4.homedir(), ".openacp", "agents.json");
|
|
14556
|
-
}
|
|
14557
|
-
load() {
|
|
14558
|
-
if (!fs12.existsSync(this.filePath)) {
|
|
14559
|
-
this.data = { version: 1, installed: {} };
|
|
14560
|
-
return;
|
|
14561
|
-
}
|
|
14562
|
-
try {
|
|
14563
|
-
const raw = JSON.parse(fs12.readFileSync(this.filePath, "utf-8"));
|
|
14564
|
-
const result = AgentStoreSchema.safeParse(raw);
|
|
14565
|
-
if (result.success) {
|
|
14566
|
-
this.data = result.data;
|
|
14567
|
-
} else {
|
|
14568
|
-
log10.warn({ errors: result.error.issues }, "Invalid agents.json, starting fresh");
|
|
14569
|
-
this.data = { version: 1, installed: {} };
|
|
14570
|
-
}
|
|
14571
|
-
} catch (err) {
|
|
14572
|
-
log10.warn({ err }, "Failed to read agents.json, starting fresh");
|
|
14573
|
-
this.data = { version: 1, installed: {} };
|
|
14574
|
-
}
|
|
14575
|
-
}
|
|
14576
|
-
exists() {
|
|
14577
|
-
return fs12.existsSync(this.filePath);
|
|
14578
|
-
}
|
|
14579
|
-
getInstalled() {
|
|
14580
|
-
return this.data.installed;
|
|
14581
|
-
}
|
|
14582
|
-
getAgent(key) {
|
|
14583
|
-
return this.data.installed[key];
|
|
14584
|
-
}
|
|
14585
|
-
addAgent(key, agent) {
|
|
14586
|
-
this.data.installed[key] = agent;
|
|
14587
|
-
this.save();
|
|
14588
|
-
}
|
|
14589
|
-
removeAgent(key) {
|
|
14590
|
-
delete this.data.installed[key];
|
|
14591
|
-
this.save();
|
|
14592
|
-
}
|
|
14593
|
-
hasAgent(key) {
|
|
14594
|
-
return key in this.data.installed;
|
|
14595
|
-
}
|
|
14596
|
-
save() {
|
|
14597
|
-
fs12.mkdirSync(path11.dirname(this.filePath), { recursive: true });
|
|
14598
|
-
const tmpPath = this.filePath + ".tmp";
|
|
14599
|
-
fs12.writeFileSync(tmpPath, JSON.stringify(this.data, null, 2), { mode: 384 });
|
|
14600
|
-
fs12.renameSync(tmpPath, this.filePath);
|
|
14601
|
-
}
|
|
14602
|
-
};
|
|
14603
|
-
|
|
14604
14669
|
// src/core/agents/agent-catalog.ts
|
|
14605
14670
|
init_agent_installer();
|
|
14606
14671
|
init_agent_dependencies();
|
|
14607
14672
|
init_log();
|
|
14608
|
-
|
|
14673
|
+
import * as fs12 from "fs";
|
|
14674
|
+
import * as path11 from "path";
|
|
14675
|
+
var log11 = createChildLogger({ module: "agent-catalog" });
|
|
14609
14676
|
var REGISTRY_URL = "https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json";
|
|
14610
14677
|
var DEFAULT_TTL_HOURS = 24;
|
|
14611
14678
|
var AgentCatalog = class {
|
|
@@ -14614,8 +14681,8 @@ var AgentCatalog = class {
|
|
|
14614
14681
|
cachePath;
|
|
14615
14682
|
agentsDir;
|
|
14616
14683
|
constructor(store, cachePath, agentsDir) {
|
|
14617
|
-
this.store = store
|
|
14618
|
-
this.cachePath = cachePath
|
|
14684
|
+
this.store = store;
|
|
14685
|
+
this.cachePath = cachePath;
|
|
14619
14686
|
this.agentsDir = agentsDir;
|
|
14620
14687
|
}
|
|
14621
14688
|
load() {
|
|
@@ -14626,7 +14693,7 @@ var AgentCatalog = class {
|
|
|
14626
14693
|
// --- Registry ---
|
|
14627
14694
|
async fetchRegistry() {
|
|
14628
14695
|
try {
|
|
14629
|
-
|
|
14696
|
+
log11.info("Fetching agent registry from CDN...");
|
|
14630
14697
|
const response = await fetch(REGISTRY_URL);
|
|
14631
14698
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
14632
14699
|
const data = await response.json();
|
|
@@ -14636,11 +14703,11 @@ var AgentCatalog = class {
|
|
|
14636
14703
|
ttlHours: DEFAULT_TTL_HOURS,
|
|
14637
14704
|
data
|
|
14638
14705
|
};
|
|
14639
|
-
|
|
14640
|
-
|
|
14641
|
-
|
|
14706
|
+
fs12.mkdirSync(path11.dirname(this.cachePath), { recursive: true });
|
|
14707
|
+
fs12.writeFileSync(this.cachePath, JSON.stringify(cache, null, 2), { mode: 384 });
|
|
14708
|
+
log11.info({ count: this.registryAgents.length }, "Registry updated");
|
|
14642
14709
|
} catch (err) {
|
|
14643
|
-
|
|
14710
|
+
log11.warn({ err }, "Failed to fetch registry, using cached data");
|
|
14644
14711
|
}
|
|
14645
14712
|
}
|
|
14646
14713
|
async refreshRegistryIfStale() {
|
|
@@ -14671,7 +14738,7 @@ var AgentCatalog = class {
|
|
|
14671
14738
|
}
|
|
14672
14739
|
// --- Discovery ---
|
|
14673
14740
|
getAvailable() {
|
|
14674
|
-
const installed = this.
|
|
14741
|
+
const installed = this.getInstalledEntries();
|
|
14675
14742
|
const items = [];
|
|
14676
14743
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
14677
14744
|
for (const [key, agent] of Object.entries(installed)) {
|
|
@@ -14745,11 +14812,11 @@ var AgentCatalog = class {
|
|
|
14745
14812
|
this.store.addAgent(key, data);
|
|
14746
14813
|
}
|
|
14747
14814
|
async uninstall(key) {
|
|
14748
|
-
if (
|
|
14749
|
-
|
|
14815
|
+
if (this.store.hasAgent(key)) {
|
|
14816
|
+
await uninstallAgent(key, this.store);
|
|
14817
|
+
return { ok: true };
|
|
14750
14818
|
}
|
|
14751
|
-
|
|
14752
|
-
return { ok: true };
|
|
14819
|
+
return { ok: false, error: `"${key}" is not installed.` };
|
|
14753
14820
|
}
|
|
14754
14821
|
// --- Resolution (for AgentManager) ---
|
|
14755
14822
|
resolve(key) {
|
|
@@ -14792,7 +14859,15 @@ var AgentCatalog = class {
|
|
|
14792
14859
|
const dist = resolveDistribution(regAgent);
|
|
14793
14860
|
if (dist) {
|
|
14794
14861
|
agent.distribution = dist.type;
|
|
14795
|
-
|
|
14862
|
+
if (dist.type === "npx") {
|
|
14863
|
+
agent.command = "npx";
|
|
14864
|
+
agent.args = [stripNpmVersion(dist.package), ...dist.args];
|
|
14865
|
+
updated = true;
|
|
14866
|
+
} else if (dist.type === "uvx") {
|
|
14867
|
+
agent.command = "uvx";
|
|
14868
|
+
agent.args = [stripPythonVersion(dist.package), ...dist.args];
|
|
14869
|
+
updated = true;
|
|
14870
|
+
}
|
|
14796
14871
|
}
|
|
14797
14872
|
}
|
|
14798
14873
|
if (updated) {
|
|
@@ -14801,13 +14876,13 @@ var AgentCatalog = class {
|
|
|
14801
14876
|
}
|
|
14802
14877
|
}
|
|
14803
14878
|
if (changed) {
|
|
14804
|
-
|
|
14879
|
+
log11.info("Enriched installed agents with registry data");
|
|
14805
14880
|
}
|
|
14806
14881
|
}
|
|
14807
14882
|
isCacheStale() {
|
|
14808
|
-
if (!
|
|
14883
|
+
if (!fs12.existsSync(this.cachePath)) return true;
|
|
14809
14884
|
try {
|
|
14810
|
-
const raw = JSON.parse(
|
|
14885
|
+
const raw = JSON.parse(fs12.readFileSync(this.cachePath, "utf-8"));
|
|
14811
14886
|
const fetchedAt = new Date(raw.fetchedAt).getTime();
|
|
14812
14887
|
const ttlMs = (raw.ttlHours ?? DEFAULT_TTL_HOURS) * 60 * 60 * 1e3;
|
|
14813
14888
|
return Date.now() - fetchedAt > ttlMs;
|
|
@@ -14816,36 +14891,127 @@ var AgentCatalog = class {
|
|
|
14816
14891
|
}
|
|
14817
14892
|
}
|
|
14818
14893
|
loadRegistryFromCacheOrSnapshot() {
|
|
14819
|
-
if (
|
|
14894
|
+
if (fs12.existsSync(this.cachePath)) {
|
|
14820
14895
|
try {
|
|
14821
|
-
const raw = JSON.parse(
|
|
14896
|
+
const raw = JSON.parse(fs12.readFileSync(this.cachePath, "utf-8"));
|
|
14822
14897
|
if (raw.data?.agents) {
|
|
14823
14898
|
this.registryAgents = raw.data.agents;
|
|
14824
|
-
|
|
14899
|
+
log11.debug({ count: this.registryAgents.length }, "Loaded registry from cache");
|
|
14825
14900
|
return;
|
|
14826
14901
|
}
|
|
14827
14902
|
} catch {
|
|
14828
|
-
|
|
14903
|
+
log11.warn("Failed to load registry cache");
|
|
14829
14904
|
}
|
|
14830
14905
|
}
|
|
14831
14906
|
try {
|
|
14832
14907
|
const candidates = [
|
|
14833
|
-
|
|
14834
|
-
|
|
14835
|
-
|
|
14908
|
+
path11.join(import.meta.dirname, "data", "registry-snapshot.json"),
|
|
14909
|
+
path11.join(import.meta.dirname, "..", "data", "registry-snapshot.json"),
|
|
14910
|
+
path11.join(import.meta.dirname, "..", "..", "data", "registry-snapshot.json")
|
|
14836
14911
|
];
|
|
14837
14912
|
for (const candidate of candidates) {
|
|
14838
|
-
if (
|
|
14839
|
-
const raw = JSON.parse(
|
|
14913
|
+
if (fs12.existsSync(candidate)) {
|
|
14914
|
+
const raw = JSON.parse(fs12.readFileSync(candidate, "utf-8"));
|
|
14840
14915
|
this.registryAgents = raw.agents ?? [];
|
|
14841
|
-
|
|
14916
|
+
log11.debug({ count: this.registryAgents.length }, "Loaded registry from bundled snapshot");
|
|
14842
14917
|
return;
|
|
14843
14918
|
}
|
|
14844
14919
|
}
|
|
14845
|
-
|
|
14920
|
+
log11.warn("No registry data available (no cache, no snapshot)");
|
|
14846
14921
|
} catch {
|
|
14847
|
-
|
|
14922
|
+
log11.warn("Failed to load bundled registry snapshot");
|
|
14923
|
+
}
|
|
14924
|
+
}
|
|
14925
|
+
};
|
|
14926
|
+
function stripNpmVersion(pkg) {
|
|
14927
|
+
if (pkg.startsWith("@")) {
|
|
14928
|
+
const slashIdx = pkg.indexOf("/");
|
|
14929
|
+
if (slashIdx === -1) return pkg;
|
|
14930
|
+
const versionAt = pkg.indexOf("@", slashIdx + 1);
|
|
14931
|
+
return versionAt === -1 ? pkg : pkg.slice(0, versionAt);
|
|
14932
|
+
}
|
|
14933
|
+
const at = pkg.indexOf("@");
|
|
14934
|
+
return at === -1 ? pkg : pkg.slice(0, at);
|
|
14935
|
+
}
|
|
14936
|
+
function stripPythonVersion(pkg) {
|
|
14937
|
+
const pyMatch = pkg.match(/^([^=@><!]+)/);
|
|
14938
|
+
if (pyMatch && pkg.includes("==")) return pyMatch[1];
|
|
14939
|
+
const at = pkg.indexOf("@");
|
|
14940
|
+
return at === -1 ? pkg : pkg.slice(0, at);
|
|
14941
|
+
}
|
|
14942
|
+
|
|
14943
|
+
// src/core/agents/agent-store.ts
|
|
14944
|
+
init_log();
|
|
14945
|
+
import * as fs13 from "fs";
|
|
14946
|
+
import * as path12 from "path";
|
|
14947
|
+
import { z as z2 } from "zod";
|
|
14948
|
+
var log12 = createChildLogger({ module: "agent-store" });
|
|
14949
|
+
var InstalledAgentSchema = z2.object({
|
|
14950
|
+
registryId: z2.string().nullable(),
|
|
14951
|
+
name: z2.string(),
|
|
14952
|
+
version: z2.string(),
|
|
14953
|
+
distribution: z2.enum(["npx", "uvx", "binary", "custom"]),
|
|
14954
|
+
command: z2.string(),
|
|
14955
|
+
args: z2.array(z2.string()).default([]),
|
|
14956
|
+
env: z2.record(z2.string(), z2.string()).default({}),
|
|
14957
|
+
workingDirectory: z2.string().optional(),
|
|
14958
|
+
installedAt: z2.string(),
|
|
14959
|
+
binaryPath: z2.string().nullable().default(null)
|
|
14960
|
+
});
|
|
14961
|
+
var AgentStoreSchema = z2.object({
|
|
14962
|
+
version: z2.number().default(1),
|
|
14963
|
+
installed: z2.record(z2.string(), InstalledAgentSchema).default({})
|
|
14964
|
+
});
|
|
14965
|
+
var AgentStore = class {
|
|
14966
|
+
data = { version: 1, installed: {} };
|
|
14967
|
+
filePath;
|
|
14968
|
+
constructor(filePath) {
|
|
14969
|
+
this.filePath = filePath;
|
|
14970
|
+
}
|
|
14971
|
+
load() {
|
|
14972
|
+
if (!fs13.existsSync(this.filePath)) {
|
|
14973
|
+
this.data = { version: 1, installed: {} };
|
|
14974
|
+
return;
|
|
14848
14975
|
}
|
|
14976
|
+
try {
|
|
14977
|
+
const raw = JSON.parse(fs13.readFileSync(this.filePath, "utf-8"));
|
|
14978
|
+
const result = AgentStoreSchema.safeParse(raw);
|
|
14979
|
+
if (result.success) {
|
|
14980
|
+
this.data = result.data;
|
|
14981
|
+
} else {
|
|
14982
|
+
log12.warn({ errors: result.error.issues }, "Invalid agents.json, starting fresh");
|
|
14983
|
+
this.data = { version: 1, installed: {} };
|
|
14984
|
+
}
|
|
14985
|
+
} catch (err) {
|
|
14986
|
+
log12.warn({ err }, "Failed to read agents.json, starting fresh");
|
|
14987
|
+
this.data = { version: 1, installed: {} };
|
|
14988
|
+
}
|
|
14989
|
+
}
|
|
14990
|
+
exists() {
|
|
14991
|
+
return fs13.existsSync(this.filePath);
|
|
14992
|
+
}
|
|
14993
|
+
getInstalled() {
|
|
14994
|
+
return this.data.installed;
|
|
14995
|
+
}
|
|
14996
|
+
getAgent(key) {
|
|
14997
|
+
return this.data.installed[key];
|
|
14998
|
+
}
|
|
14999
|
+
addAgent(key, agent) {
|
|
15000
|
+
this.data.installed[key] = agent;
|
|
15001
|
+
this.save();
|
|
15002
|
+
}
|
|
15003
|
+
removeAgent(key) {
|
|
15004
|
+
delete this.data.installed[key];
|
|
15005
|
+
this.save();
|
|
15006
|
+
}
|
|
15007
|
+
hasAgent(key) {
|
|
15008
|
+
return key in this.data.installed;
|
|
15009
|
+
}
|
|
15010
|
+
save() {
|
|
15011
|
+
fs13.mkdirSync(path12.dirname(this.filePath), { recursive: true });
|
|
15012
|
+
const tmpPath = this.filePath + ".tmp";
|
|
15013
|
+
fs13.writeFileSync(tmpPath, JSON.stringify(this.data, null, 2), { mode: 384 });
|
|
15014
|
+
fs13.renameSync(tmpPath, this.filePath);
|
|
14849
15015
|
}
|
|
14850
15016
|
};
|
|
14851
15017
|
|
|
@@ -14855,7 +15021,7 @@ var EventBus = class extends TypedEmitter {
|
|
|
14855
15021
|
|
|
14856
15022
|
// src/core/plugin/plugin-loader.ts
|
|
14857
15023
|
import { createHash } from "crypto";
|
|
14858
|
-
import { readFileSync as
|
|
15024
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
14859
15025
|
function resolveLoadOrder(plugins) {
|
|
14860
15026
|
const overrideTargets = /* @__PURE__ */ new Set();
|
|
14861
15027
|
for (const p of plugins) {
|
|
@@ -15096,32 +15262,28 @@ var ErrorTracker = class {
|
|
|
15096
15262
|
}
|
|
15097
15263
|
};
|
|
15098
15264
|
|
|
15099
|
-
// src/core/plugin/plugin-context.ts
|
|
15100
|
-
import path15 from "path";
|
|
15101
|
-
import os7 from "os";
|
|
15102
|
-
|
|
15103
15265
|
// src/core/plugin/plugin-storage.ts
|
|
15104
|
-
import
|
|
15105
|
-
import
|
|
15266
|
+
import fs14 from "fs";
|
|
15267
|
+
import path13 from "path";
|
|
15106
15268
|
var PluginStorageImpl = class {
|
|
15107
15269
|
kvPath;
|
|
15108
15270
|
dataDir;
|
|
15109
15271
|
writeChain = Promise.resolve();
|
|
15110
15272
|
constructor(baseDir) {
|
|
15111
|
-
this.dataDir =
|
|
15112
|
-
this.kvPath =
|
|
15113
|
-
|
|
15273
|
+
this.dataDir = path13.join(baseDir, "data");
|
|
15274
|
+
this.kvPath = path13.join(baseDir, "kv.json");
|
|
15275
|
+
fs14.mkdirSync(baseDir, { recursive: true });
|
|
15114
15276
|
}
|
|
15115
15277
|
readKv() {
|
|
15116
15278
|
try {
|
|
15117
|
-
const raw =
|
|
15279
|
+
const raw = fs14.readFileSync(this.kvPath, "utf-8");
|
|
15118
15280
|
return JSON.parse(raw);
|
|
15119
15281
|
} catch {
|
|
15120
15282
|
return {};
|
|
15121
15283
|
}
|
|
15122
15284
|
}
|
|
15123
15285
|
writeKv(data) {
|
|
15124
|
-
|
|
15286
|
+
fs14.writeFileSync(this.kvPath, JSON.stringify(data), "utf-8");
|
|
15125
15287
|
}
|
|
15126
15288
|
async get(key) {
|
|
15127
15289
|
const data = this.readKv();
|
|
@@ -15147,7 +15309,7 @@ var PluginStorageImpl = class {
|
|
|
15147
15309
|
return Object.keys(this.readKv());
|
|
15148
15310
|
}
|
|
15149
15311
|
getDataDir() {
|
|
15150
|
-
|
|
15312
|
+
fs14.mkdirSync(this.dataDir, { recursive: true });
|
|
15151
15313
|
return this.dataDir;
|
|
15152
15314
|
}
|
|
15153
15315
|
};
|
|
@@ -15171,7 +15333,7 @@ function createPluginContext(opts) {
|
|
|
15171
15333
|
config,
|
|
15172
15334
|
core
|
|
15173
15335
|
} = opts;
|
|
15174
|
-
const instanceRoot = opts.instanceRoot
|
|
15336
|
+
const instanceRoot = opts.instanceRoot;
|
|
15175
15337
|
const registeredListeners = [];
|
|
15176
15338
|
const registeredCommands = [];
|
|
15177
15339
|
const registeredMenuItemIds = [];
|
|
@@ -15194,7 +15356,7 @@ function createPluginContext(opts) {
|
|
|
15194
15356
|
}
|
|
15195
15357
|
};
|
|
15196
15358
|
const baseLog = opts.log ?? noopLog;
|
|
15197
|
-
const
|
|
15359
|
+
const log37 = typeof baseLog.child === "function" ? baseLog.child({ plugin: pluginName }) : baseLog;
|
|
15198
15360
|
const storageImpl = new PluginStorageImpl(storagePath);
|
|
15199
15361
|
const storage = {
|
|
15200
15362
|
async get(key) {
|
|
@@ -15221,7 +15383,7 @@ function createPluginContext(opts) {
|
|
|
15221
15383
|
const ctx = {
|
|
15222
15384
|
pluginName,
|
|
15223
15385
|
pluginConfig,
|
|
15224
|
-
log:
|
|
15386
|
+
log: log37,
|
|
15225
15387
|
storage,
|
|
15226
15388
|
on(event, handler) {
|
|
15227
15389
|
requirePermission(permissions, "events:read", "on()");
|
|
@@ -15256,7 +15418,7 @@ function createPluginContext(opts) {
|
|
|
15256
15418
|
const registry = serviceRegistry.get("command-registry");
|
|
15257
15419
|
if (registry && typeof registry.register === "function") {
|
|
15258
15420
|
registry.register(def, pluginName);
|
|
15259
|
-
|
|
15421
|
+
log37.debug(`Command '/${def.name}' registered`);
|
|
15260
15422
|
}
|
|
15261
15423
|
},
|
|
15262
15424
|
async sendMessage(_sessionId, _content) {
|
|
@@ -15299,7 +15461,7 @@ function createPluginContext(opts) {
|
|
|
15299
15461
|
const registry = serviceRegistry.get("field-registry");
|
|
15300
15462
|
if (registry && typeof registry.register === "function") {
|
|
15301
15463
|
registry.register(pluginName, fields);
|
|
15302
|
-
|
|
15464
|
+
log37.debug(`Registered ${fields.length} editable field(s) for ${pluginName}`);
|
|
15303
15465
|
}
|
|
15304
15466
|
},
|
|
15305
15467
|
get sessions() {
|
|
@@ -15355,13 +15517,13 @@ init_events();
|
|
|
15355
15517
|
var SETUP_TIMEOUT_MS = 3e4;
|
|
15356
15518
|
var TEARDOWN_TIMEOUT_MS = 1e4;
|
|
15357
15519
|
function withTimeout(promise, ms, label) {
|
|
15358
|
-
return new Promise((
|
|
15520
|
+
return new Promise((resolve7, reject) => {
|
|
15359
15521
|
const timer = setTimeout(() => reject(new Error(`Timeout: ${label} exceeded ${ms}ms`)), ms);
|
|
15360
15522
|
if (typeof timer === "object" && timer !== null && "unref" in timer) {
|
|
15361
15523
|
;
|
|
15362
15524
|
timer.unref();
|
|
15363
15525
|
}
|
|
15364
|
-
promise.then(
|
|
15526
|
+
promise.then(resolve7, reject).finally(() => clearTimeout(timer));
|
|
15365
15527
|
});
|
|
15366
15528
|
}
|
|
15367
15529
|
function resolvePluginConfig(pluginName, configManager) {
|
|
@@ -15682,7 +15844,7 @@ Examples:
|
|
|
15682
15844
|
${baseCmd} api status
|
|
15683
15845
|
${baseCmd} api new claude-code ~/my-project --channel <current_channel>
|
|
15684
15846
|
${baseCmd} api cancel <id>
|
|
15685
|
-
${baseCmd} config set
|
|
15847
|
+
${baseCmd} config set logging.level debug
|
|
15686
15848
|
${baseCmd} agents install gemini
|
|
15687
15849
|
\`\`\`
|
|
15688
15850
|
|
|
@@ -15855,10 +16017,10 @@ function createConfigSection(core) {
|
|
|
15855
16017
|
title: "Configuration",
|
|
15856
16018
|
priority: 30,
|
|
15857
16019
|
buildContext: () => {
|
|
15858
|
-
const
|
|
16020
|
+
const workspace = core.configManager.resolveWorkspace();
|
|
15859
16021
|
const speechSvc = core.lifecycleManager?.serviceRegistry.get("speech");
|
|
15860
16022
|
const sttActive = speechSvc ? speechSvc.isSTTAvailable() : false;
|
|
15861
|
-
return `Workspace base: ${
|
|
16023
|
+
return `Workspace base: ${workspace}
|
|
15862
16024
|
STT: ${sttActive ? "configured \u2705" : "Not configured"}`;
|
|
15863
16025
|
},
|
|
15864
16026
|
commands: [
|
|
@@ -16043,13 +16205,13 @@ var OpenACPCore = class {
|
|
|
16043
16205
|
this.instanceContext = ctx;
|
|
16044
16206
|
const config = configManager.get();
|
|
16045
16207
|
this.agentCatalog = new AgentCatalog(
|
|
16046
|
-
|
|
16047
|
-
ctx
|
|
16048
|
-
ctx
|
|
16208
|
+
new AgentStore(ctx.paths.agents),
|
|
16209
|
+
ctx.paths.registryCache,
|
|
16210
|
+
ctx.paths.agentsDir
|
|
16049
16211
|
);
|
|
16050
16212
|
this.agentCatalog.load();
|
|
16051
16213
|
this.agentManager = new AgentManager(this.agentCatalog);
|
|
16052
|
-
const storePath = ctx
|
|
16214
|
+
const storePath = ctx.paths.sessions;
|
|
16053
16215
|
this.sessionStore = new JsonFileSessionStore(
|
|
16054
16216
|
storePath,
|
|
16055
16217
|
config.sessionStore.ttlDays
|
|
@@ -16063,7 +16225,7 @@ var OpenACPCore = class {
|
|
|
16063
16225
|
this.sessionManager,
|
|
16064
16226
|
() => this.speechService,
|
|
16065
16227
|
this.eventBus,
|
|
16066
|
-
ctx
|
|
16228
|
+
ctx.root
|
|
16067
16229
|
);
|
|
16068
16230
|
this.lifecycleManager = new LifecycleManager({
|
|
16069
16231
|
serviceRegistry: new ServiceRegistry(),
|
|
@@ -16073,8 +16235,8 @@ var OpenACPCore = class {
|
|
|
16073
16235
|
sessions: this.sessionManager,
|
|
16074
16236
|
config: this.configManager,
|
|
16075
16237
|
core: this,
|
|
16076
|
-
storagePath: ctx
|
|
16077
|
-
instanceRoot: ctx
|
|
16238
|
+
storagePath: ctx.paths.pluginsData,
|
|
16239
|
+
instanceRoot: ctx.root,
|
|
16078
16240
|
log: createChildLogger({ module: "plugin" })
|
|
16079
16241
|
});
|
|
16080
16242
|
this.sessionFactory.middlewareChain = this.lifecycleManager.middlewareChain;
|
|
@@ -16138,9 +16300,7 @@ var OpenACPCore = class {
|
|
|
16138
16300
|
}
|
|
16139
16301
|
);
|
|
16140
16302
|
registerCoreMenuItems(this.menuRegistry);
|
|
16141
|
-
|
|
16142
|
-
this.assistantRegistry.setInstanceRoot(path16.dirname(ctx.root));
|
|
16143
|
-
}
|
|
16303
|
+
this.assistantRegistry.setInstanceRoot(path14.dirname(ctx.root));
|
|
16144
16304
|
this.assistantRegistry.register(createSessionsSection(this));
|
|
16145
16305
|
this.assistantRegistry.register(createAgentsSection(this));
|
|
16146
16306
|
this.assistantRegistry.register(createConfigSection(this));
|
|
@@ -16439,8 +16599,8 @@ ${text3}`;
|
|
|
16439
16599
|
message: `Agent '${agentName}' not found`
|
|
16440
16600
|
};
|
|
16441
16601
|
}
|
|
16442
|
-
const { existsSync:
|
|
16443
|
-
if (!
|
|
16602
|
+
const { existsSync: existsSync18 } = await import("fs");
|
|
16603
|
+
if (!existsSync18(cwd)) {
|
|
16444
16604
|
return {
|
|
16445
16605
|
ok: false,
|
|
16446
16606
|
error: "invalid_cwd",
|
|
@@ -16786,19 +16946,30 @@ init_doctor();
|
|
|
16786
16946
|
init_config_registry();
|
|
16787
16947
|
|
|
16788
16948
|
// src/core/config/config-editor.ts
|
|
16789
|
-
import * as
|
|
16949
|
+
import * as path30 from "path";
|
|
16790
16950
|
import * as clack2 from "@clack/prompts";
|
|
16791
16951
|
|
|
16792
16952
|
// src/cli/autostart.ts
|
|
16793
16953
|
init_log();
|
|
16794
16954
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
16795
|
-
import * as
|
|
16796
|
-
import * as
|
|
16797
|
-
import * as
|
|
16955
|
+
import * as fs25 from "fs";
|
|
16956
|
+
import * as path23 from "path";
|
|
16957
|
+
import * as os7 from "os";
|
|
16798
16958
|
var log18 = createChildLogger({ module: "autostart" });
|
|
16799
|
-
var
|
|
16800
|
-
var
|
|
16801
|
-
|
|
16959
|
+
var LEGACY_LAUNCHD_PLIST_PATH = path23.join(os7.homedir(), "Library", "LaunchAgents", "com.openacp.daemon.plist");
|
|
16960
|
+
var LEGACY_SYSTEMD_SERVICE_PATH = path23.join(os7.homedir(), ".config", "systemd", "user", "openacp.service");
|
|
16961
|
+
function getLaunchdLabel(instanceId) {
|
|
16962
|
+
return `com.openacp.daemon.${instanceId}`;
|
|
16963
|
+
}
|
|
16964
|
+
function getLaunchdPlistPath(instanceId) {
|
|
16965
|
+
return path23.join(os7.homedir(), "Library", "LaunchAgents", `${getLaunchdLabel(instanceId)}.plist`);
|
|
16966
|
+
}
|
|
16967
|
+
function getSystemdServiceName(instanceId) {
|
|
16968
|
+
return `openacp-${instanceId}`;
|
|
16969
|
+
}
|
|
16970
|
+
function getSystemdServicePath(instanceId) {
|
|
16971
|
+
return path23.join(os7.homedir(), ".config", "systemd", "user", `${getSystemdServiceName(instanceId)}.service`);
|
|
16972
|
+
}
|
|
16802
16973
|
function isAutoStartSupported() {
|
|
16803
16974
|
return process.platform === "darwin" || process.platform === "linux";
|
|
16804
16975
|
}
|
|
@@ -16809,20 +16980,26 @@ function escapeSystemdValue(str) {
|
|
|
16809
16980
|
const escaped = str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\$/g, "$$$$").replace(/%/g, "%%");
|
|
16810
16981
|
return `"${escaped}"`;
|
|
16811
16982
|
}
|
|
16812
|
-
function generateLaunchdPlist(nodePath, cliPath, logDir2) {
|
|
16813
|
-
const
|
|
16983
|
+
function generateLaunchdPlist(nodePath, cliPath, logDir2, instanceRoot, instanceId) {
|
|
16984
|
+
const label = getLaunchdLabel(instanceId);
|
|
16985
|
+
const logFile = path23.join(logDir2, "openacp.log");
|
|
16814
16986
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
16815
16987
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
16816
16988
|
<plist version="1.0">
|
|
16817
16989
|
<dict>
|
|
16818
16990
|
<key>Label</key>
|
|
16819
|
-
<string>${
|
|
16991
|
+
<string>${label}</string>
|
|
16820
16992
|
<key>ProgramArguments</key>
|
|
16821
16993
|
<array>
|
|
16822
16994
|
<string>${escapeXml(nodePath)}</string>
|
|
16823
16995
|
<string>${escapeXml(cliPath)}</string>
|
|
16824
16996
|
<string>--daemon-child</string>
|
|
16825
16997
|
</array>
|
|
16998
|
+
<key>EnvironmentVariables</key>
|
|
16999
|
+
<dict>
|
|
17000
|
+
<key>OPENACP_INSTANCE_ROOT</key>
|
|
17001
|
+
<string>${escapeXml(instanceRoot)}</string>
|
|
17002
|
+
</dict>
|
|
16826
17003
|
<key>RunAtLoad</key>
|
|
16827
17004
|
<true/>
|
|
16828
17005
|
<key>KeepAlive</key>
|
|
@@ -16838,43 +17015,85 @@ function generateLaunchdPlist(nodePath, cliPath, logDir2) {
|
|
|
16838
17015
|
</plist>
|
|
16839
17016
|
`;
|
|
16840
17017
|
}
|
|
16841
|
-
function generateSystemdUnit(nodePath, cliPath) {
|
|
17018
|
+
function generateSystemdUnit(nodePath, cliPath, instanceRoot, instanceId) {
|
|
17019
|
+
const serviceName = getSystemdServiceName(instanceId);
|
|
16842
17020
|
return `[Unit]
|
|
16843
|
-
Description=OpenACP Daemon
|
|
17021
|
+
Description=OpenACP Daemon (${instanceId})
|
|
16844
17022
|
|
|
16845
17023
|
[Service]
|
|
16846
17024
|
ExecStart=${escapeSystemdValue(nodePath)} ${escapeSystemdValue(cliPath)} --daemon-child
|
|
17025
|
+
Environment=OPENACP_INSTANCE_ROOT=${escapeSystemdValue(instanceRoot)}
|
|
16847
17026
|
Restart=on-failure
|
|
16848
17027
|
|
|
16849
17028
|
[Install]
|
|
16850
17029
|
WantedBy=default.target
|
|
17030
|
+
# Service name: ${serviceName}
|
|
16851
17031
|
`;
|
|
16852
17032
|
}
|
|
16853
|
-
function
|
|
17033
|
+
function migrateLegacy() {
|
|
17034
|
+
if (process.platform === "darwin" && fs25.existsSync(LEGACY_LAUNCHD_PLIST_PATH)) {
|
|
17035
|
+
try {
|
|
17036
|
+
const uid = process.getuid();
|
|
17037
|
+
execFileSync6("launchctl", ["bootout", `gui/${uid}`, "com.openacp.daemon"], { stdio: "pipe" });
|
|
17038
|
+
} catch {
|
|
17039
|
+
}
|
|
17040
|
+
try {
|
|
17041
|
+
fs25.unlinkSync(LEGACY_LAUNCHD_PLIST_PATH);
|
|
17042
|
+
} catch {
|
|
17043
|
+
}
|
|
17044
|
+
log18.info("Removed legacy single-instance LaunchAgent");
|
|
17045
|
+
}
|
|
17046
|
+
if (process.platform === "linux" && fs25.existsSync(LEGACY_SYSTEMD_SERVICE_PATH)) {
|
|
17047
|
+
try {
|
|
17048
|
+
execFileSync6("systemctl", ["--user", "disable", "openacp"], { stdio: "pipe" });
|
|
17049
|
+
} catch {
|
|
17050
|
+
}
|
|
17051
|
+
try {
|
|
17052
|
+
fs25.unlinkSync(LEGACY_SYSTEMD_SERVICE_PATH);
|
|
17053
|
+
} catch {
|
|
17054
|
+
}
|
|
17055
|
+
try {
|
|
17056
|
+
execFileSync6("systemctl", ["--user", "daemon-reload"], { stdio: "pipe" });
|
|
17057
|
+
} catch {
|
|
17058
|
+
}
|
|
17059
|
+
log18.info("Removed legacy single-instance systemd service");
|
|
17060
|
+
}
|
|
17061
|
+
}
|
|
17062
|
+
function installAutoStart(logDir2, instanceRoot, instanceId) {
|
|
16854
17063
|
if (!isAutoStartSupported()) {
|
|
16855
17064
|
return { success: false, error: "Auto-start not supported on this platform" };
|
|
16856
17065
|
}
|
|
16857
17066
|
const nodePath = process.execPath;
|
|
16858
|
-
const cliPath =
|
|
16859
|
-
const resolvedLogDir = logDir2.startsWith("~") ?
|
|
17067
|
+
const cliPath = path23.resolve(process.argv[1]);
|
|
17068
|
+
const resolvedLogDir = logDir2.startsWith("~") ? path23.join(os7.homedir(), logDir2.slice(1)) : logDir2;
|
|
16860
17069
|
try {
|
|
17070
|
+
migrateLegacy();
|
|
16861
17071
|
if (process.platform === "darwin") {
|
|
16862
|
-
const
|
|
16863
|
-
const
|
|
16864
|
-
|
|
16865
|
-
|
|
16866
|
-
|
|
16867
|
-
|
|
17072
|
+
const plistPath = getLaunchdPlistPath(instanceId);
|
|
17073
|
+
const plist = generateLaunchdPlist(nodePath, cliPath, resolvedLogDir, instanceRoot, instanceId);
|
|
17074
|
+
const dir = path23.dirname(plistPath);
|
|
17075
|
+
fs25.mkdirSync(dir, { recursive: true });
|
|
17076
|
+
fs25.writeFileSync(plistPath, plist);
|
|
17077
|
+
const uid = process.getuid();
|
|
17078
|
+
const domain = `gui/${uid}`;
|
|
17079
|
+
try {
|
|
17080
|
+
execFileSync6("launchctl", ["bootout", domain, plistPath], { stdio: "pipe" });
|
|
17081
|
+
} catch {
|
|
17082
|
+
}
|
|
17083
|
+
execFileSync6("launchctl", ["bootstrap", domain, plistPath], { stdio: "pipe" });
|
|
17084
|
+
log18.info({ instanceId }, "LaunchAgent installed");
|
|
16868
17085
|
return { success: true };
|
|
16869
17086
|
}
|
|
16870
17087
|
if (process.platform === "linux") {
|
|
16871
|
-
const
|
|
16872
|
-
const
|
|
16873
|
-
|
|
16874
|
-
|
|
17088
|
+
const servicePath = getSystemdServicePath(instanceId);
|
|
17089
|
+
const serviceName = getSystemdServiceName(instanceId);
|
|
17090
|
+
const unit = generateSystemdUnit(nodePath, cliPath, instanceRoot, instanceId);
|
|
17091
|
+
const dir = path23.dirname(servicePath);
|
|
17092
|
+
fs25.mkdirSync(dir, { recursive: true });
|
|
17093
|
+
fs25.writeFileSync(servicePath, unit);
|
|
16875
17094
|
execFileSync6("systemctl", ["--user", "daemon-reload"], { stdio: "pipe" });
|
|
16876
|
-
execFileSync6("systemctl", ["--user", "enable",
|
|
16877
|
-
log18.info("systemd user service installed");
|
|
17095
|
+
execFileSync6("systemctl", ["--user", "enable", serviceName], { stdio: "pipe" });
|
|
17096
|
+
log18.info({ instanceId }, "systemd user service installed");
|
|
16878
17097
|
return { success: true };
|
|
16879
17098
|
}
|
|
16880
17099
|
return { success: false, error: "Unsupported platform" };
|
|
@@ -16884,31 +17103,35 @@ function installAutoStart(logDir2) {
|
|
|
16884
17103
|
return { success: false, error: msg };
|
|
16885
17104
|
}
|
|
16886
17105
|
}
|
|
16887
|
-
function uninstallAutoStart() {
|
|
17106
|
+
function uninstallAutoStart(instanceId) {
|
|
16888
17107
|
if (!isAutoStartSupported()) {
|
|
16889
17108
|
return { success: false, error: "Auto-start not supported on this platform" };
|
|
16890
17109
|
}
|
|
16891
17110
|
try {
|
|
16892
17111
|
if (process.platform === "darwin") {
|
|
16893
|
-
|
|
17112
|
+
const plistPath = getLaunchdPlistPath(instanceId);
|
|
17113
|
+
if (fs25.existsSync(plistPath)) {
|
|
17114
|
+
const uid = process.getuid();
|
|
16894
17115
|
try {
|
|
16895
|
-
execFileSync6("launchctl", ["
|
|
17116
|
+
execFileSync6("launchctl", ["bootout", `gui/${uid}`, plistPath], { stdio: "pipe" });
|
|
16896
17117
|
} catch {
|
|
16897
17118
|
}
|
|
16898
|
-
|
|
16899
|
-
log18.info("LaunchAgent removed");
|
|
17119
|
+
fs25.unlinkSync(plistPath);
|
|
17120
|
+
log18.info({ instanceId }, "LaunchAgent removed");
|
|
16900
17121
|
}
|
|
16901
17122
|
return { success: true };
|
|
16902
17123
|
}
|
|
16903
17124
|
if (process.platform === "linux") {
|
|
16904
|
-
|
|
17125
|
+
const servicePath = getSystemdServicePath(instanceId);
|
|
17126
|
+
const serviceName = getSystemdServiceName(instanceId);
|
|
17127
|
+
if (fs25.existsSync(servicePath)) {
|
|
16905
17128
|
try {
|
|
16906
|
-
execFileSync6("systemctl", ["--user", "disable",
|
|
17129
|
+
execFileSync6("systemctl", ["--user", "disable", serviceName], { stdio: "pipe" });
|
|
16907
17130
|
} catch {
|
|
16908
17131
|
}
|
|
16909
|
-
|
|
17132
|
+
fs25.unlinkSync(servicePath);
|
|
16910
17133
|
execFileSync6("systemctl", ["--user", "daemon-reload"], { stdio: "pipe" });
|
|
16911
|
-
log18.info("systemd user service removed");
|
|
17134
|
+
log18.info({ instanceId }, "systemd user service removed");
|
|
16912
17135
|
}
|
|
16913
17136
|
return { success: true };
|
|
16914
17137
|
}
|
|
@@ -16919,16 +17142,41 @@ function uninstallAutoStart() {
|
|
|
16919
17142
|
return { success: false, error: msg };
|
|
16920
17143
|
}
|
|
16921
17144
|
}
|
|
16922
|
-
function isAutoStartInstalled() {
|
|
17145
|
+
function isAutoStartInstalled(instanceId) {
|
|
16923
17146
|
if (process.platform === "darwin") {
|
|
16924
|
-
return
|
|
17147
|
+
return fs25.existsSync(getLaunchdPlistPath(instanceId));
|
|
16925
17148
|
}
|
|
16926
17149
|
if (process.platform === "linux") {
|
|
16927
|
-
return
|
|
17150
|
+
return fs25.existsSync(getSystemdServicePath(instanceId));
|
|
16928
17151
|
}
|
|
16929
17152
|
return false;
|
|
16930
17153
|
}
|
|
16931
17154
|
|
|
17155
|
+
// src/cli/resolve-instance-id.ts
|
|
17156
|
+
init_instance_context();
|
|
17157
|
+
init_instance_registry();
|
|
17158
|
+
init_log();
|
|
17159
|
+
import fs28 from "fs";
|
|
17160
|
+
import path26 from "path";
|
|
17161
|
+
var log19 = createChildLogger({ module: "resolve-instance-id" });
|
|
17162
|
+
function resolveInstanceId(instanceRoot) {
|
|
17163
|
+
try {
|
|
17164
|
+
const configPath = path26.join(instanceRoot, "config.json");
|
|
17165
|
+
const raw = JSON.parse(fs28.readFileSync(configPath, "utf-8"));
|
|
17166
|
+
if (raw.id && typeof raw.id === "string") return raw.id;
|
|
17167
|
+
} catch {
|
|
17168
|
+
}
|
|
17169
|
+
try {
|
|
17170
|
+
const reg = new InstanceRegistry(path26.join(getGlobalRoot(), "instances.json"));
|
|
17171
|
+
reg.load();
|
|
17172
|
+
const entry = reg.getByRoot(instanceRoot);
|
|
17173
|
+
if (entry?.id) return entry.id;
|
|
17174
|
+
} catch (err) {
|
|
17175
|
+
log19.debug({ err: err.message, instanceRoot }, "Could not read instance registry, using fallback id");
|
|
17176
|
+
}
|
|
17177
|
+
return path26.basename(path26.dirname(instanceRoot)).replace(/[^a-zA-Z0-9-]/g, "-") || "default";
|
|
17178
|
+
}
|
|
17179
|
+
|
|
16932
17180
|
// src/core/config/config-editor.ts
|
|
16933
17181
|
init_config();
|
|
16934
17182
|
async function select3(opts) {
|
|
@@ -17054,29 +17302,28 @@ async function ensureDiscordPlugin() {
|
|
|
17054
17302
|
}
|
|
17055
17303
|
}
|
|
17056
17304
|
}
|
|
17057
|
-
async function editDiscord(_config, _updates) {
|
|
17305
|
+
async function editDiscord(_config, _updates, settingsManager, instanceRoot) {
|
|
17058
17306
|
const pluginModule = await ensureDiscordPlugin();
|
|
17059
17307
|
if (!pluginModule) return;
|
|
17060
17308
|
const plugin = pluginModule.default;
|
|
17061
17309
|
if (plugin?.configure) {
|
|
17062
|
-
|
|
17310
|
+
if (!settingsManager || !instanceRoot) {
|
|
17311
|
+
console.log(warn("Cannot configure Discord \u2014 instance context not available."));
|
|
17312
|
+
return;
|
|
17313
|
+
}
|
|
17063
17314
|
const { createInstallContext: createInstallContext2 } = await Promise.resolve().then(() => (init_install_context(), install_context_exports));
|
|
17064
|
-
const { getGlobalRoot: getGlobalRoot2 } = await Promise.resolve().then(() => (init_instance_context(), instance_context_exports));
|
|
17065
|
-
const root = getGlobalRoot2();
|
|
17066
|
-
const basePath = path28.join(root, "plugins", "data");
|
|
17067
|
-
const settingsManager = new SettingsManager2(basePath);
|
|
17068
17315
|
const ctx = createInstallContext2({
|
|
17069
17316
|
pluginName: plugin.name,
|
|
17070
17317
|
settingsManager,
|
|
17071
|
-
basePath,
|
|
17072
|
-
instanceRoot
|
|
17318
|
+
basePath: settingsManager.getBasePath(),
|
|
17319
|
+
instanceRoot
|
|
17073
17320
|
});
|
|
17074
17321
|
await plugin.configure(ctx);
|
|
17075
17322
|
} else {
|
|
17076
17323
|
console.log(warn("This plugin does not have a configure() method yet."));
|
|
17077
17324
|
}
|
|
17078
17325
|
}
|
|
17079
|
-
async function editChannels(config, updates, settingsManager) {
|
|
17326
|
+
async function editChannels(config, updates, settingsManager, instanceRoot) {
|
|
17080
17327
|
let tgConfigured = false;
|
|
17081
17328
|
let dcConfigured = false;
|
|
17082
17329
|
if (settingsManager) {
|
|
@@ -17100,7 +17347,7 @@ async function editChannels(config, updates, settingsManager) {
|
|
|
17100
17347
|
});
|
|
17101
17348
|
if (choice === "back") break;
|
|
17102
17349
|
if (choice === "telegram") await editTelegram(config, updates, settingsManager);
|
|
17103
|
-
if (choice === "discord") await editDiscord(config, updates);
|
|
17350
|
+
if (choice === "discord") await editDiscord(config, updates, settingsManager, instanceRoot);
|
|
17104
17351
|
}
|
|
17105
17352
|
}
|
|
17106
17353
|
async function editAgent(config, updates) {
|
|
@@ -17133,19 +17380,6 @@ async function editAgent(config, updates) {
|
|
|
17133
17380
|
}
|
|
17134
17381
|
}
|
|
17135
17382
|
}
|
|
17136
|
-
async function editWorkspace(config, updates) {
|
|
17137
|
-
const currentDir = config.workspace?.baseDir ?? "~/openacp-workspace";
|
|
17138
|
-
console.log(header("Workspace"));
|
|
17139
|
-
console.log(` Base directory : ${currentDir}`);
|
|
17140
|
-
console.log("");
|
|
17141
|
-
const newDir = await input({
|
|
17142
|
-
message: "Workspace base directory:",
|
|
17143
|
-
default: currentDir,
|
|
17144
|
-
validate: (val) => val.trim().length > 0 || "Path cannot be empty"
|
|
17145
|
-
});
|
|
17146
|
-
updates.workspace = { baseDir: newDir.trim() };
|
|
17147
|
-
console.log(ok(`Workspace set to ${newDir.trim()}`));
|
|
17148
|
-
}
|
|
17149
17383
|
async function editSecurity(config, updates, settingsManager) {
|
|
17150
17384
|
const ps = settingsManager ? await settingsManager.loadSettings("@openacp/security") : {};
|
|
17151
17385
|
const sec = {
|
|
@@ -17244,10 +17478,11 @@ async function editLogging(config, updates) {
|
|
|
17244
17478
|
}
|
|
17245
17479
|
}
|
|
17246
17480
|
}
|
|
17247
|
-
async function editRunMode(config, updates) {
|
|
17481
|
+
async function editRunMode(config, updates, instanceRoot) {
|
|
17248
17482
|
const currentMode = config.runMode ?? "foreground";
|
|
17249
17483
|
const currentAutoStart = config.autoStart ?? false;
|
|
17250
|
-
const
|
|
17484
|
+
const instanceId = instanceRoot ? resolveInstanceId(instanceRoot) : "default";
|
|
17485
|
+
const autoStartInstalled = isAutoStartInstalled(instanceId);
|
|
17251
17486
|
const autoStartSupported = isAutoStartSupported();
|
|
17252
17487
|
console.log(header("Run Mode"));
|
|
17253
17488
|
console.log(` Current mode : ${c.bold}${currentMode}${c.reset}`);
|
|
@@ -17280,7 +17515,7 @@ async function editRunMode(config, updates) {
|
|
|
17280
17515
|
if (choice === "daemon") {
|
|
17281
17516
|
updates.runMode = "daemon";
|
|
17282
17517
|
const logDir2 = config.logging?.logDir ?? "~/.openacp/logs";
|
|
17283
|
-
const result = installAutoStart(
|
|
17518
|
+
const result = installAutoStart(expandHome2(logDir2), instanceRoot, instanceId);
|
|
17284
17519
|
if (result.success) {
|
|
17285
17520
|
updates.autoStart = true;
|
|
17286
17521
|
console.log(ok("Switched to daemon mode with auto-start"));
|
|
@@ -17291,7 +17526,7 @@ async function editRunMode(config, updates) {
|
|
|
17291
17526
|
if (choice === "foreground") {
|
|
17292
17527
|
updates.runMode = "foreground";
|
|
17293
17528
|
updates.autoStart = false;
|
|
17294
|
-
uninstallAutoStart();
|
|
17529
|
+
uninstallAutoStart(instanceId);
|
|
17295
17530
|
console.log(ok("Switched to foreground mode"));
|
|
17296
17531
|
}
|
|
17297
17532
|
if (choice === "toggleAutoStart") {
|
|
@@ -17300,7 +17535,7 @@ async function editRunMode(config, updates) {
|
|
|
17300
17535
|
return currentAutoStart;
|
|
17301
17536
|
})();
|
|
17302
17537
|
if (autoStartCurrent) {
|
|
17303
|
-
const result = uninstallAutoStart();
|
|
17538
|
+
const result = uninstallAutoStart(instanceId);
|
|
17304
17539
|
updates.autoStart = false;
|
|
17305
17540
|
if (result.success) {
|
|
17306
17541
|
console.log(ok("Auto-start disabled"));
|
|
@@ -17309,7 +17544,7 @@ async function editRunMode(config, updates) {
|
|
|
17309
17544
|
}
|
|
17310
17545
|
} else {
|
|
17311
17546
|
const logDir2 = config.logging?.logDir ?? "~/.openacp/logs";
|
|
17312
|
-
const result = installAutoStart(
|
|
17547
|
+
const result = installAutoStart(expandHome2(logDir2), instanceRoot, instanceId);
|
|
17313
17548
|
updates.autoStart = result.success;
|
|
17314
17549
|
if (result.success) {
|
|
17315
17550
|
console.log(ok("Auto-start enabled"));
|
|
@@ -17511,6 +17746,7 @@ async function runConfigEditor(configManager, mode = "file", apiPort, settingsMa
|
|
|
17511
17746
|
await configManager.load();
|
|
17512
17747
|
const config = configManager.get();
|
|
17513
17748
|
const updates = {};
|
|
17749
|
+
const instanceRoot = path30.dirname(configManager.getConfigPath());
|
|
17514
17750
|
console.log(`
|
|
17515
17751
|
${c.cyan}${c.bold}OpenACP Config Editor${c.reset}`);
|
|
17516
17752
|
console.log(dim(`Config: ${configManager.getConfigPath()}`));
|
|
@@ -17523,7 +17759,6 @@ ${c.cyan}${c.bold}OpenACP Config Editor${c.reset}`);
|
|
|
17523
17759
|
choices: [
|
|
17524
17760
|
{ name: "Channels", value: "channels" },
|
|
17525
17761
|
{ name: "Agent", value: "agent" },
|
|
17526
|
-
{ name: "Workspace", value: "workspace" },
|
|
17527
17762
|
{ name: "Security", value: "security" },
|
|
17528
17763
|
{ name: "Logging", value: "logging" },
|
|
17529
17764
|
{ name: "Run Mode", value: "runMode" },
|
|
@@ -17542,12 +17777,11 @@ ${c.cyan}${c.bold}OpenACP Config Editor${c.reset}`);
|
|
|
17542
17777
|
break;
|
|
17543
17778
|
}
|
|
17544
17779
|
const sectionUpdates = {};
|
|
17545
|
-
if (choice === "channels") await editChannels(config, sectionUpdates, settingsManager);
|
|
17780
|
+
if (choice === "channels") await editChannels(config, sectionUpdates, settingsManager, instanceRoot);
|
|
17546
17781
|
else if (choice === "agent") await editAgent(config, sectionUpdates);
|
|
17547
|
-
else if (choice === "workspace") await editWorkspace(config, sectionUpdates);
|
|
17548
17782
|
else if (choice === "security") await editSecurity(config, sectionUpdates, settingsManager);
|
|
17549
17783
|
else if (choice === "logging") await editLogging(config, sectionUpdates);
|
|
17550
|
-
else if (choice === "runMode") await editRunMode(config, sectionUpdates);
|
|
17784
|
+
else if (choice === "runMode") await editRunMode(config, sectionUpdates, instanceRoot);
|
|
17551
17785
|
else if (choice === "api") await editApi(config, sectionUpdates, settingsManager);
|
|
17552
17786
|
else if (choice === "tunnel") await editTunnel(config, sectionUpdates, settingsManager);
|
|
17553
17787
|
if (mode === "api" && Object.keys(sectionUpdates).length > 0) {
|
|
@@ -17569,17 +17803,17 @@ ${c.cyan}${c.bold}OpenACP Config Editor${c.reset}`);
|
|
|
17569
17803
|
async function sendConfigViaApi(port, updates) {
|
|
17570
17804
|
const { apiCall: call } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
17571
17805
|
const paths = flattenToPaths(updates);
|
|
17572
|
-
for (const { path:
|
|
17806
|
+
for (const { path: path35, value } of paths) {
|
|
17573
17807
|
const res = await call(port, "/api/config", {
|
|
17574
17808
|
method: "PATCH",
|
|
17575
17809
|
headers: { "Content-Type": "application/json" },
|
|
17576
|
-
body: JSON.stringify({ path:
|
|
17810
|
+
body: JSON.stringify({ path: path35, value })
|
|
17577
17811
|
});
|
|
17578
17812
|
const data = await res.json();
|
|
17579
17813
|
if (!res.ok) {
|
|
17580
|
-
console.log(warn(`Failed to update ${
|
|
17814
|
+
console.log(warn(`Failed to update ${path35}: ${data.error}`));
|
|
17581
17815
|
} else if (data.needsRestart) {
|
|
17582
|
-
console.log(warn(`${
|
|
17816
|
+
console.log(warn(`${path35} updated \u2014 restart required`));
|
|
17583
17817
|
}
|
|
17584
17818
|
}
|
|
17585
17819
|
}
|
|
@@ -17599,30 +17833,25 @@ function flattenToPaths(obj, prefix = "") {
|
|
|
17599
17833
|
// src/cli/daemon.ts
|
|
17600
17834
|
init_config();
|
|
17601
17835
|
import { spawn as spawn3 } from "child_process";
|
|
17602
|
-
import * as
|
|
17603
|
-
import * as
|
|
17604
|
-
import * as os14 from "os";
|
|
17605
|
-
var DEFAULT_ROOT2 = path29.join(os14.homedir(), ".openacp");
|
|
17836
|
+
import * as fs31 from "fs";
|
|
17837
|
+
import * as path31 from "path";
|
|
17606
17838
|
function getPidPath(root) {
|
|
17607
|
-
|
|
17608
|
-
return path29.join(base, "openacp.pid");
|
|
17839
|
+
return path31.join(root, "openacp.pid");
|
|
17609
17840
|
}
|
|
17610
17841
|
function getLogDir(root) {
|
|
17611
|
-
|
|
17612
|
-
return path29.join(base, "logs");
|
|
17842
|
+
return path31.join(root, "logs");
|
|
17613
17843
|
}
|
|
17614
17844
|
function getRunningMarker(root) {
|
|
17615
|
-
|
|
17616
|
-
return path29.join(base, "running");
|
|
17845
|
+
return path31.join(root, "running");
|
|
17617
17846
|
}
|
|
17618
17847
|
function writePidFile(pidPath, pid) {
|
|
17619
|
-
const dir =
|
|
17620
|
-
|
|
17621
|
-
|
|
17848
|
+
const dir = path31.dirname(pidPath);
|
|
17849
|
+
fs31.mkdirSync(dir, { recursive: true });
|
|
17850
|
+
fs31.writeFileSync(pidPath, String(pid));
|
|
17622
17851
|
}
|
|
17623
17852
|
function readPidFile(pidPath) {
|
|
17624
17853
|
try {
|
|
17625
|
-
const content =
|
|
17854
|
+
const content = fs31.readFileSync(pidPath, "utf-8").trim();
|
|
17626
17855
|
const pid = parseInt(content, 10);
|
|
17627
17856
|
return isNaN(pid) ? null : pid;
|
|
17628
17857
|
} catch {
|
|
@@ -17631,7 +17860,7 @@ function readPidFile(pidPath) {
|
|
|
17631
17860
|
}
|
|
17632
17861
|
function removePidFile(pidPath) {
|
|
17633
17862
|
try {
|
|
17634
|
-
|
|
17863
|
+
fs31.unlinkSync(pidPath);
|
|
17635
17864
|
} catch {
|
|
17636
17865
|
}
|
|
17637
17866
|
}
|
|
@@ -17646,7 +17875,7 @@ function isProcessRunning(pidPath) {
|
|
|
17646
17875
|
return false;
|
|
17647
17876
|
}
|
|
17648
17877
|
}
|
|
17649
|
-
function getStatus(pidPath
|
|
17878
|
+
function getStatus(pidPath) {
|
|
17650
17879
|
const pid = readPidFile(pidPath);
|
|
17651
17880
|
if (pid === null) return { running: false };
|
|
17652
17881
|
try {
|
|
@@ -17657,19 +17886,19 @@ function getStatus(pidPath = getPidPath()) {
|
|
|
17657
17886
|
return { running: false };
|
|
17658
17887
|
}
|
|
17659
17888
|
}
|
|
17660
|
-
function startDaemon(pidPath
|
|
17889
|
+
function startDaemon(pidPath, logDir2, instanceRoot) {
|
|
17661
17890
|
markRunning(instanceRoot);
|
|
17662
17891
|
if (isProcessRunning(pidPath)) {
|
|
17663
17892
|
const pid = readPidFile(pidPath);
|
|
17664
17893
|
return { error: `Already running (PID ${pid})` };
|
|
17665
17894
|
}
|
|
17666
|
-
const resolvedLogDir = logDir2 ?
|
|
17667
|
-
|
|
17668
|
-
const logFile =
|
|
17669
|
-
const cliPath =
|
|
17895
|
+
const resolvedLogDir = logDir2 ? expandHome2(logDir2) : getLogDir(instanceRoot);
|
|
17896
|
+
fs31.mkdirSync(resolvedLogDir, { recursive: true });
|
|
17897
|
+
const logFile = path31.join(resolvedLogDir, "openacp.log");
|
|
17898
|
+
const cliPath = path31.resolve(process.argv[1]);
|
|
17670
17899
|
const nodePath = process.execPath;
|
|
17671
|
-
const out =
|
|
17672
|
-
const err =
|
|
17900
|
+
const out = fs31.openSync(logFile, "a");
|
|
17901
|
+
const err = fs31.openSync(logFile, "a");
|
|
17673
17902
|
const child = spawn3(nodePath, [cliPath, "--daemon-child"], {
|
|
17674
17903
|
detached: true,
|
|
17675
17904
|
stdio: ["ignore", out, err],
|
|
@@ -17678,8 +17907,8 @@ function startDaemon(pidPath = getPidPath(), logDir2, instanceRoot) {
|
|
|
17678
17907
|
...instanceRoot ? { OPENACP_INSTANCE_ROOT: instanceRoot } : {}
|
|
17679
17908
|
}
|
|
17680
17909
|
});
|
|
17681
|
-
|
|
17682
|
-
|
|
17910
|
+
fs31.closeSync(out);
|
|
17911
|
+
fs31.closeSync(err);
|
|
17683
17912
|
if (!child.pid) {
|
|
17684
17913
|
return { error: "Failed to spawn daemon process" };
|
|
17685
17914
|
}
|
|
@@ -17687,8 +17916,8 @@ function startDaemon(pidPath = getPidPath(), logDir2, instanceRoot) {
|
|
|
17687
17916
|
child.unref();
|
|
17688
17917
|
return { pid: child.pid };
|
|
17689
17918
|
}
|
|
17690
|
-
function
|
|
17691
|
-
return new Promise((
|
|
17919
|
+
function sleep2(ms) {
|
|
17920
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
17692
17921
|
}
|
|
17693
17922
|
function isProcessAlive2(pid) {
|
|
17694
17923
|
try {
|
|
@@ -17700,7 +17929,7 @@ function isProcessAlive2(pid) {
|
|
|
17700
17929
|
return "dead";
|
|
17701
17930
|
}
|
|
17702
17931
|
}
|
|
17703
|
-
async function stopDaemon(pidPath
|
|
17932
|
+
async function stopDaemon(pidPath, instanceRoot) {
|
|
17704
17933
|
const pid = readPidFile(pidPath);
|
|
17705
17934
|
if (pid === null) return { stopped: false, error: "Not running (no PID file)" };
|
|
17706
17935
|
const status = isProcessAlive2(pid);
|
|
@@ -17722,7 +17951,7 @@ async function stopDaemon(pidPath = getPidPath(), instanceRoot) {
|
|
|
17722
17951
|
const TIMEOUT = 5e3;
|
|
17723
17952
|
const start = Date.now();
|
|
17724
17953
|
while (Date.now() - start < TIMEOUT) {
|
|
17725
|
-
await
|
|
17954
|
+
await sleep2(POLL_INTERVAL);
|
|
17726
17955
|
const s = isProcessAlive2(pid);
|
|
17727
17956
|
if (s === "dead" || s === "eperm") {
|
|
17728
17957
|
removePidFile(pidPath);
|
|
@@ -17739,7 +17968,7 @@ async function stopDaemon(pidPath = getPidPath(), instanceRoot) {
|
|
|
17739
17968
|
}
|
|
17740
17969
|
const killStart = Date.now();
|
|
17741
17970
|
while (Date.now() - killStart < 1e3) {
|
|
17742
|
-
await
|
|
17971
|
+
await sleep2(POLL_INTERVAL);
|
|
17743
17972
|
const s = isProcessAlive2(pid);
|
|
17744
17973
|
if (s === "dead" || s === "eperm") {
|
|
17745
17974
|
removePidFile(pidPath);
|
|
@@ -17750,12 +17979,12 @@ async function stopDaemon(pidPath = getPidPath(), instanceRoot) {
|
|
|
17750
17979
|
}
|
|
17751
17980
|
function markRunning(root) {
|
|
17752
17981
|
const marker = getRunningMarker(root);
|
|
17753
|
-
|
|
17754
|
-
|
|
17982
|
+
fs31.mkdirSync(path31.dirname(marker), { recursive: true });
|
|
17983
|
+
fs31.writeFileSync(marker, "");
|
|
17755
17984
|
}
|
|
17756
17985
|
function clearRunning(root) {
|
|
17757
17986
|
try {
|
|
17758
|
-
|
|
17987
|
+
fs31.unlinkSync(getRunningMarker(root));
|
|
17759
17988
|
} catch {
|
|
17760
17989
|
}
|
|
17761
17990
|
}
|
|
@@ -17771,7 +18000,7 @@ init_static_server();
|
|
|
17771
18000
|
|
|
17772
18001
|
// src/plugins/telegram/topic-manager.ts
|
|
17773
18002
|
init_log();
|
|
17774
|
-
var
|
|
18003
|
+
var log22 = createChildLogger({ module: "topic-manager" });
|
|
17775
18004
|
var TopicManager = class {
|
|
17776
18005
|
constructor(sessionManager, adapter, systemTopicIds) {
|
|
17777
18006
|
this.sessionManager = sessionManager;
|
|
@@ -17810,7 +18039,7 @@ var TopicManager = class {
|
|
|
17810
18039
|
try {
|
|
17811
18040
|
await this.adapter.deleteSessionThread?.(sessionId);
|
|
17812
18041
|
} catch (err) {
|
|
17813
|
-
|
|
18042
|
+
log22.warn({ err, sessionId, topicId }, "Failed to delete platform thread, removing record anyway");
|
|
17814
18043
|
}
|
|
17815
18044
|
}
|
|
17816
18045
|
await this.sessionManager.removeRecord(sessionId);
|
|
@@ -17833,7 +18062,7 @@ var TopicManager = class {
|
|
|
17833
18062
|
try {
|
|
17834
18063
|
await this.adapter.deleteSessionThread?.(record.sessionId);
|
|
17835
18064
|
} catch (err) {
|
|
17836
|
-
|
|
18065
|
+
log22.warn({ err, sessionId: record.sessionId }, "Failed to delete platform thread during cleanup");
|
|
17837
18066
|
}
|
|
17838
18067
|
}
|
|
17839
18068
|
await this.sessionManager.removeRecord(record.sessionId);
|
|
@@ -18455,7 +18684,7 @@ Config file: \`~/.openacp/config.json\`
|
|
|
18455
18684
|
- Agent list is fetched from the ACP Registry CDN and cached locally (24h)
|
|
18456
18685
|
|
|
18457
18686
|
### Workspace
|
|
18458
|
-
-
|
|
18687
|
+
- Workspace directory is the parent of \`.openacp/\` (where you ran \`openacp\` setup)
|
|
18459
18688
|
|
|
18460
18689
|
### Security
|
|
18461
18690
|
- **security.allowedUserIds** \u2014 Restrict who can use the bot (empty = everyone)
|
|
@@ -18611,7 +18840,7 @@ export {
|
|
|
18611
18840
|
createChildLogger,
|
|
18612
18841
|
createSessionLogger,
|
|
18613
18842
|
createTurnContext,
|
|
18614
|
-
|
|
18843
|
+
expandHome2 as expandHome,
|
|
18615
18844
|
extractContentText,
|
|
18616
18845
|
formatTokens,
|
|
18617
18846
|
formatToolSummary,
|