@cjhyy/code-shell-core 0.8.8 → 0.8.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/automation/scheduler.js +49 -0
- package/dist/automation/store.d.ts +1 -1
- package/dist/automation/store.js +184 -10
- package/dist/cli/agent-server-stdio.js +7 -0
- package/dist/credentials/store.d.ts +14 -0
- package/dist/credentials/store.js +245 -42
- package/dist/engine/engine.js +81 -27
- package/dist/engine/file-history-hook.js +24 -5
- package/dist/engine/run-finalize.js +8 -6
- package/dist/engine/run-session-open.js +1 -1
- package/dist/engine/run-setup.d.ts +4 -0
- package/dist/engine/run-setup.js +5 -1
- package/dist/engine/run-tooling.js +7 -1
- package/dist/engine/run-types.d.ts +33 -0
- package/dist/engine/run-types.js +21 -0
- package/dist/engine/run-workspace.js +11 -5
- package/dist/engine/turn-loop.js +9 -8
- package/dist/goal/lifecycle.d.ts +2 -0
- package/dist/goal/lifecycle.js +56 -33
- package/dist/index.d.ts +2 -3
- package/dist/index.internal.d.ts +1 -0
- package/dist/index.internal.js +1 -0
- package/dist/index.js +2 -2
- package/dist/links/cli.d.ts +2 -0
- package/dist/links/cli.js +11 -4
- package/dist/model-catalog/index.js +19 -4
- package/dist/model-catalog/save-entry.js +122 -61
- package/dist/model-catalog/types.js +27 -23
- package/dist/panel-apps/installer.js +27 -14
- package/dist/panel-apps/manifest.d.ts +11 -11
- package/dist/panel-apps/manifest.js +3 -1
- package/dist/panel-apps/registry.js +60 -12
- package/dist/plugins/installedPlugins.d.ts +4 -0
- package/dist/plugins/installedPlugins.js +70 -30
- package/dist/plugins/installer/types.d.ts +12 -12
- package/dist/plugins/installer/update.js +37 -38
- package/dist/plugins/knownMarketplaces.d.ts +7 -3
- package/dist/plugins/knownMarketplaces.js +127 -23
- package/dist/plugins/pluginCatalog.js +18 -4
- package/dist/plugins/pluginHookApproval.js +56 -60
- package/dist/plugins/pluginMcpApproval.js +50 -52
- package/dist/profile/catalog-store.js +39 -4
- package/dist/profile/catalog.js +55 -15
- package/dist/profile/store.js +51 -21
- package/dist/prompt/composer.d.ts +8 -0
- package/dist/prompt/composer.js +8 -0
- package/dist/protocol/chat-session-manager.d.ts +9 -0
- package/dist/protocol/chat-session-manager.js +22 -0
- package/dist/protocol/chat-session.d.ts +5 -0
- package/dist/protocol/chat-session.js +1 -0
- package/dist/protocol/server.d.ts +2 -0
- package/dist/protocol/server.js +112 -29
- package/dist/protocol/types.d.ts +20 -0
- package/dist/run/FileRunStore.d.ts +2 -0
- package/dist/run/FileRunStore.js +153 -18
- package/dist/run/Heartbeat.js +63 -4
- package/dist/services/auto-dream.js +39 -17
- package/dist/services/session-memory.js +107 -8
- package/dist/session/file-history.d.ts +63 -2
- package/dist/session/file-history.js +593 -86
- package/dist/session/session-manager.d.ts +4 -1
- package/dist/session/session-manager.js +94 -47
- package/dist/session/transcript.js +33 -3
- package/dist/session/undo-target.d.ts +15 -6
- package/dist/session/undo-target.js +26 -9
- package/dist/settings/manager.d.ts +22 -3
- package/dist/settings/manager.js +185 -50
- package/dist/settings/schema.d.ts +3 -3
- package/dist/sources/adapters/local-files.js +49 -4
- package/dist/sources/catalog.js +64 -18
- package/dist/sources/types.d.ts +3 -3
- package/dist/sources/types.js +7 -4
- package/dist/themes/installer.js +192 -28
- package/dist/tool-system/builtin/add-marketplace.js +21 -1
- package/dist/tool-system/builtin/cron.d.ts +2 -1
- package/dist/tool-system/builtin/cron.js +20 -6
- package/dist/tool-system/builtin/index.js +48 -1
- package/dist/tool-system/builtin/install-capability.d.ts +52 -0
- package/dist/tool-system/builtin/install-capability.js +1057 -0
- package/dist/tool-system/builtin/skill.js +3 -1
- package/dist/tool-system/executor.js +1 -0
- package/dist/tool-system/path-policy.js +25 -33
- package/dist/tool-system/registry.js +5 -0
- package/dist/utils/file-mutex.d.ts +2 -0
- package/dist/utils/file-mutex.js +29 -4
- package/package.json +2 -1
|
@@ -38,6 +38,50 @@ const MAX_SCHEDULE_INTERVAL_MS = 10 * 365 * 24 * 60 * 60 * 1000;
|
|
|
38
38
|
export function isCronMisfire(scheduledFor, now) {
|
|
39
39
|
return now - scheduledFor > CRON_MISFIRE_GRACE_MS;
|
|
40
40
|
}
|
|
41
|
+
const MAX_JOB_NAME_CHARS = 512;
|
|
42
|
+
const MAX_JOB_SCHEDULE_CHARS = 512;
|
|
43
|
+
const MAX_JOB_PROMPT_CHARS = 1024 * 1024;
|
|
44
|
+
const MAX_JOB_CWD_CHARS = 32_768;
|
|
45
|
+
const MAX_JOB_TIMEZONE_CHARS = 128;
|
|
46
|
+
function validateJobFields(input) {
|
|
47
|
+
if (input.name !== undefined &&
|
|
48
|
+
(typeof input.name !== "string" ||
|
|
49
|
+
!input.name.trim() ||
|
|
50
|
+
input.name.length > MAX_JOB_NAME_CHARS ||
|
|
51
|
+
input.name.includes("\0"))) {
|
|
52
|
+
throw new Error("automation name must be a bounded non-empty string");
|
|
53
|
+
}
|
|
54
|
+
if (input.schedule !== undefined &&
|
|
55
|
+
(typeof input.schedule !== "string" ||
|
|
56
|
+
!input.schedule.trim() ||
|
|
57
|
+
input.schedule.length > MAX_JOB_SCHEDULE_CHARS ||
|
|
58
|
+
input.schedule.includes("\0"))) {
|
|
59
|
+
throw new Error("automation schedule must be a bounded non-empty string");
|
|
60
|
+
}
|
|
61
|
+
if (input.prompt !== undefined &&
|
|
62
|
+
(typeof input.prompt !== "string" ||
|
|
63
|
+
!input.prompt.trim() ||
|
|
64
|
+
input.prompt.length > MAX_JOB_PROMPT_CHARS ||
|
|
65
|
+
input.prompt.includes("\0"))) {
|
|
66
|
+
throw new Error("automation prompt must be a bounded non-empty string");
|
|
67
|
+
}
|
|
68
|
+
if (input.cwd !== undefined &&
|
|
69
|
+
(typeof input.cwd !== "string" || input.cwd.length > MAX_JOB_CWD_CHARS || input.cwd.includes("\0"))) {
|
|
70
|
+
throw new Error("automation cwd must be a bounded string");
|
|
71
|
+
}
|
|
72
|
+
if (input.timezone !== undefined &&
|
|
73
|
+
(typeof input.timezone !== "string" ||
|
|
74
|
+
input.timezone.length > MAX_JOB_TIMEZONE_CHARS ||
|
|
75
|
+
input.timezone.includes("\0"))) {
|
|
76
|
+
throw new Error("automation timezone must be a bounded string");
|
|
77
|
+
}
|
|
78
|
+
if (input.permissionLevel !== undefined &&
|
|
79
|
+
input.permissionLevel !== "read-only" &&
|
|
80
|
+
input.permissionLevel !== "workspace-write" &&
|
|
81
|
+
input.permissionLevel !== "full") {
|
|
82
|
+
throw new Error("invalid automation permission level");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
41
85
|
export class CronScheduler {
|
|
42
86
|
jobs = new Map();
|
|
43
87
|
timers = new Map();
|
|
@@ -301,6 +345,7 @@ export class CronScheduler {
|
|
|
301
345
|
}
|
|
302
346
|
}
|
|
303
347
|
create(name, schedule, prompt, opts) {
|
|
348
|
+
validateJobFields({ name, schedule, prompt, ...opts });
|
|
304
349
|
// Validate the schedule up front (interval or cron expr) so a bad string
|
|
305
350
|
// surfaces at create time, not silently at the first missed tick.
|
|
306
351
|
validateSchedule(schedule, opts?.timezone);
|
|
@@ -459,6 +504,10 @@ export class CronScheduler {
|
|
|
459
504
|
* or null if the id is unknown.
|
|
460
505
|
*/
|
|
461
506
|
update(id, patch) {
|
|
507
|
+
if (!patch || typeof patch !== "object" || Array.isArray(patch)) {
|
|
508
|
+
throw new Error("automation update patch must be an object");
|
|
509
|
+
}
|
|
510
|
+
validateJobFields(patch);
|
|
462
511
|
if (this.store) {
|
|
463
512
|
const tx = this.store.mutate((jobs) => {
|
|
464
513
|
let updated = null;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* the same lock and one process cannot overwrite another process's new job
|
|
12
12
|
* with a stale in-memory snapshot.
|
|
13
13
|
*/
|
|
14
|
-
import type
|
|
14
|
+
import { type CronJob } from "./scheduler.js";
|
|
15
15
|
/**
|
|
16
16
|
* Default global location. Mirrors FileRunStore's `~/.code-shell/...` layout.
|
|
17
17
|
* An explicit `root` (a `~/.code-shell`-equivalent data root) overrides the
|
package/dist/automation/store.js
CHANGED
|
@@ -11,11 +11,145 @@
|
|
|
11
11
|
* the same lock and one process cannot overwrite another process's new job
|
|
12
12
|
* with a stale in-memory snapshot.
|
|
13
13
|
*/
|
|
14
|
-
import { mkdirSync, existsSync, readFileSync, writeFileSync, renameSync, rmSync } from "node:fs";
|
|
14
|
+
import { chmodSync, mkdirSync, existsSync, readFileSync, statSync, writeFileSync, renameSync, rmSync, } from "node:fs";
|
|
15
|
+
import { randomUUID } from "node:crypto";
|
|
15
16
|
import { dirname, join } from "node:path";
|
|
16
17
|
import { homedir } from "node:os";
|
|
18
|
+
import { validateSchedule } from "./scheduler.js";
|
|
17
19
|
import { logger } from "../logging/logger.js";
|
|
18
20
|
import { lockSync } from "../utils/lockfile.js";
|
|
21
|
+
const MAX_CRON_FILE_BYTES = 32 * 1024 * 1024;
|
|
22
|
+
const MAX_CRON_JOBS = 4_096;
|
|
23
|
+
const SAFE_ID = /^[A-Za-z0-9_.-]{1,128}$/;
|
|
24
|
+
function normalizeJob(value, strict) {
|
|
25
|
+
const invalid = (field) => {
|
|
26
|
+
if (strict)
|
|
27
|
+
throw new Error(`invalid cron job ${field}`);
|
|
28
|
+
return undefined;
|
|
29
|
+
};
|
|
30
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
31
|
+
return invalid("object");
|
|
32
|
+
const raw = value;
|
|
33
|
+
if (typeof raw.id !== "string" || !SAFE_ID.test(raw.id) || raw.id.includes("..")) {
|
|
34
|
+
return invalid("id");
|
|
35
|
+
}
|
|
36
|
+
if (typeof raw.name !== "string" ||
|
|
37
|
+
!raw.name.trim() ||
|
|
38
|
+
raw.name.length > 512 ||
|
|
39
|
+
raw.name.includes("\0")) {
|
|
40
|
+
return invalid("name");
|
|
41
|
+
}
|
|
42
|
+
if (typeof raw.schedule !== "string" ||
|
|
43
|
+
!raw.schedule.trim() ||
|
|
44
|
+
raw.schedule.length > 512 ||
|
|
45
|
+
raw.schedule.includes("\0")) {
|
|
46
|
+
return invalid("schedule");
|
|
47
|
+
}
|
|
48
|
+
if (typeof raw.prompt !== "string" ||
|
|
49
|
+
!raw.prompt.trim() ||
|
|
50
|
+
raw.prompt.length > 1024 * 1024 ||
|
|
51
|
+
raw.prompt.includes("\0")) {
|
|
52
|
+
return invalid("prompt");
|
|
53
|
+
}
|
|
54
|
+
if (typeof raw.enabled !== "boolean")
|
|
55
|
+
return invalid("enabled");
|
|
56
|
+
if (!Number.isSafeInteger(raw.runCount) || raw.runCount < 0) {
|
|
57
|
+
return invalid("runCount");
|
|
58
|
+
}
|
|
59
|
+
if (!Number.isSafeInteger(raw.createdAt) || raw.createdAt < 0) {
|
|
60
|
+
return invalid("createdAt");
|
|
61
|
+
}
|
|
62
|
+
if (raw.cwd !== undefined &&
|
|
63
|
+
(typeof raw.cwd !== "string" || raw.cwd.length > 32_768 || raw.cwd.includes("\0"))) {
|
|
64
|
+
return invalid("cwd");
|
|
65
|
+
}
|
|
66
|
+
if (raw.timezone !== undefined &&
|
|
67
|
+
(typeof raw.timezone !== "string" || raw.timezone.length > 128 || raw.timezone.includes("\0"))) {
|
|
68
|
+
return invalid("timezone");
|
|
69
|
+
}
|
|
70
|
+
if (raw.permissionLevel !== undefined &&
|
|
71
|
+
raw.permissionLevel !== "read-only" &&
|
|
72
|
+
raw.permissionLevel !== "workspace-write" &&
|
|
73
|
+
raw.permissionLevel !== "full") {
|
|
74
|
+
return invalid("permissionLevel");
|
|
75
|
+
}
|
|
76
|
+
for (const field of ["lastRun", "nextRun"]) {
|
|
77
|
+
if (raw[field] !== undefined &&
|
|
78
|
+
(!Number.isSafeInteger(raw[field]) || raw[field] < 0)) {
|
|
79
|
+
return invalid(field);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
for (const field of ["lastRunId", "resumeSessionId"]) {
|
|
83
|
+
if (raw[field] !== undefined &&
|
|
84
|
+
(typeof raw[field] !== "string" ||
|
|
85
|
+
!raw[field] ||
|
|
86
|
+
raw[field].length > 128 ||
|
|
87
|
+
raw[field].includes("\0"))) {
|
|
88
|
+
return invalid(field);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (raw.once !== undefined && typeof raw.once !== "boolean")
|
|
92
|
+
return invalid("once");
|
|
93
|
+
if (raw.disabledReason !== undefined &&
|
|
94
|
+
(typeof raw.disabledReason !== "string" || raw.disabledReason.length > 4_096)) {
|
|
95
|
+
return invalid("disabledReason");
|
|
96
|
+
}
|
|
97
|
+
let templateSource;
|
|
98
|
+
if (raw.templateSource !== undefined) {
|
|
99
|
+
if (!raw.templateSource || typeof raw.templateSource !== "object" || Array.isArray(raw.templateSource)) {
|
|
100
|
+
return invalid("templateSource");
|
|
101
|
+
}
|
|
102
|
+
const source = raw.templateSource;
|
|
103
|
+
if (typeof source.installKey !== "string" ||
|
|
104
|
+
!source.installKey ||
|
|
105
|
+
source.installKey.length > 512 ||
|
|
106
|
+
typeof source.templateId !== "string" ||
|
|
107
|
+
!source.templateId ||
|
|
108
|
+
source.templateId.length > 512 ||
|
|
109
|
+
typeof source.revision !== "string" ||
|
|
110
|
+
!source.revision ||
|
|
111
|
+
source.revision.length > 512 ||
|
|
112
|
+
(source.pluginVersion !== undefined &&
|
|
113
|
+
(typeof source.pluginVersion !== "string" || source.pluginVersion.length > 128))) {
|
|
114
|
+
return invalid("templateSource");
|
|
115
|
+
}
|
|
116
|
+
templateSource = {
|
|
117
|
+
installKey: source.installKey,
|
|
118
|
+
templateId: source.templateId,
|
|
119
|
+
revision: source.revision,
|
|
120
|
+
...(typeof source.pluginVersion === "string" ? { pluginVersion: source.pluginVersion } : {}),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
validateSchedule(raw.schedule, typeof raw.timezone === "string" ? raw.timezone : undefined);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return invalid("schedule");
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
id: raw.id,
|
|
131
|
+
name: raw.name,
|
|
132
|
+
schedule: raw.schedule,
|
|
133
|
+
prompt: raw.prompt,
|
|
134
|
+
enabled: raw.enabled,
|
|
135
|
+
runCount: raw.runCount,
|
|
136
|
+
createdAt: raw.createdAt,
|
|
137
|
+
...(typeof raw.lastRun === "number" ? { lastRun: raw.lastRun } : {}),
|
|
138
|
+
...(typeof raw.nextRun === "number" ? { nextRun: raw.nextRun } : {}),
|
|
139
|
+
...(typeof raw.cwd === "string" ? { cwd: raw.cwd } : {}),
|
|
140
|
+
...(typeof raw.timezone === "string" ? { timezone: raw.timezone } : {}),
|
|
141
|
+
...(raw.permissionLevel === "read-only" ||
|
|
142
|
+
raw.permissionLevel === "workspace-write" ||
|
|
143
|
+
raw.permissionLevel === "full"
|
|
144
|
+
? { permissionLevel: raw.permissionLevel }
|
|
145
|
+
: {}),
|
|
146
|
+
...(typeof raw.lastRunId === "string" ? { lastRunId: raw.lastRunId } : {}),
|
|
147
|
+
...(raw.once === true ? { once: true } : {}),
|
|
148
|
+
...(typeof raw.resumeSessionId === "string" ? { resumeSessionId: raw.resumeSessionId } : {}),
|
|
149
|
+
...(typeof raw.disabledReason === "string" ? { disabledReason: raw.disabledReason } : {}),
|
|
150
|
+
...(templateSource ? { templateSource } : {}),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
19
153
|
/**
|
|
20
154
|
* Default global location. Mirrors FileRunStore's `~/.code-shell/...` layout.
|
|
21
155
|
* An explicit `root` (a `~/.code-shell`-equivalent data root) overrides the
|
|
@@ -64,11 +198,23 @@ export class CronStore {
|
|
|
64
198
|
if (!existsSync(this.file))
|
|
65
199
|
return [];
|
|
66
200
|
try {
|
|
201
|
+
if (statSync(this.file).size > MAX_CRON_FILE_BYTES) {
|
|
202
|
+
throw new Error("cron store exceeds the maximum file size");
|
|
203
|
+
}
|
|
67
204
|
const raw = readFileSync(this.file, "utf-8");
|
|
68
205
|
const parsed = JSON.parse(raw);
|
|
69
206
|
if (!parsed || !Array.isArray(parsed.jobs))
|
|
70
207
|
return [];
|
|
71
|
-
|
|
208
|
+
const jobs = [];
|
|
209
|
+
const ids = new Set();
|
|
210
|
+
for (const value of parsed.jobs.slice(0, MAX_CRON_JOBS)) {
|
|
211
|
+
const job = normalizeJob(value, false);
|
|
212
|
+
if (!job || ids.has(job.id))
|
|
213
|
+
continue;
|
|
214
|
+
ids.add(job.id);
|
|
215
|
+
jobs.push(job);
|
|
216
|
+
}
|
|
217
|
+
return jobs;
|
|
72
218
|
}
|
|
73
219
|
catch (err) {
|
|
74
220
|
// Corrupt snapshot — log and start fresh rather than crashing startup.
|
|
@@ -81,25 +227,53 @@ export class CronStore {
|
|
|
81
227
|
}
|
|
82
228
|
}
|
|
83
229
|
saveUnlocked(jobs) {
|
|
230
|
+
if (!Array.isArray(jobs) || jobs.length > MAX_CRON_JOBS) {
|
|
231
|
+
throw new Error("cron store exceeds the maximum job count");
|
|
232
|
+
}
|
|
233
|
+
const normalized = [];
|
|
234
|
+
const ids = new Set();
|
|
235
|
+
let estimatedBytes = 32;
|
|
236
|
+
for (const value of jobs) {
|
|
237
|
+
const job = normalizeJob(value, true);
|
|
238
|
+
if (ids.has(job.id))
|
|
239
|
+
throw new Error(`duplicate cron job id: ${job.id}`);
|
|
240
|
+
ids.add(job.id);
|
|
241
|
+
estimatedBytes += Buffer.byteLength(JSON.stringify(job)) + 2;
|
|
242
|
+
if (estimatedBytes > MAX_CRON_FILE_BYTES) {
|
|
243
|
+
throw new Error("cron store exceeds the maximum file size");
|
|
244
|
+
}
|
|
245
|
+
normalized.push(job);
|
|
246
|
+
}
|
|
84
247
|
const dir = dirname(this.file);
|
|
85
248
|
if (!existsSync(dir))
|
|
86
|
-
mkdirSync(dir, { recursive: true });
|
|
87
|
-
|
|
249
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
250
|
+
if (process.platform !== "win32")
|
|
251
|
+
chmodSync(dir, 0o700);
|
|
252
|
+
const snapshot = { version: 1, jobs: normalized };
|
|
88
253
|
// Unique tmp name so a concurrent writer can't clobber our staging file.
|
|
89
|
-
const tmp = `${this.file}.${process.pid}.tmp`;
|
|
254
|
+
const tmp = `${this.file}.${process.pid}.${randomUUID()}.tmp`;
|
|
90
255
|
try {
|
|
91
|
-
writeFileSync(tmp, JSON.stringify(snapshot, null, 2) + "\n",
|
|
256
|
+
writeFileSync(tmp, JSON.stringify(snapshot, null, 2) + "\n", {
|
|
257
|
+
encoding: "utf-8",
|
|
258
|
+
mode: 0o600,
|
|
259
|
+
});
|
|
92
260
|
renameSync(tmp, this.file);
|
|
93
261
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
262
|
+
finally {
|
|
263
|
+
try {
|
|
264
|
+
rmSync(tmp, { force: true });
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
// Preserve the original persistence error.
|
|
268
|
+
}
|
|
97
269
|
}
|
|
98
270
|
}
|
|
99
271
|
acquireStoreLock() {
|
|
100
272
|
const dir = dirname(this.file);
|
|
101
273
|
if (!existsSync(dir))
|
|
102
|
-
mkdirSync(dir, { recursive: true });
|
|
274
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
275
|
+
if (process.platform !== "win32")
|
|
276
|
+
chmodSync(dir, 0o700);
|
|
103
277
|
const deadline = Date.now() + 1_000;
|
|
104
278
|
let lastError;
|
|
105
279
|
while (Date.now() <= deadline) {
|
|
@@ -40,6 +40,7 @@ import { StdioTransport } from "../protocol/transport.js";
|
|
|
40
40
|
import { createNotification, Methods } from "../protocol/types.js";
|
|
41
41
|
import { setCronChangedSink } from "../tool-system/builtin/cron.js";
|
|
42
42
|
import { setModelCatalogChangedSink } from "../tool-system/builtin/edit-model-catalog.js";
|
|
43
|
+
import { setCapabilityChangedSink } from "../tool-system/builtin/install-capability.js";
|
|
43
44
|
import { SettingsManager, noRepoDir } from "../settings/manager.js";
|
|
44
45
|
import { personalizationFrom } from "../settings/personalization.js";
|
|
45
46
|
import { MCPManager } from "../tool-system/mcp-manager.js";
|
|
@@ -338,6 +339,12 @@ setCronChangedSink(() => {
|
|
|
338
339
|
setModelCatalogChangedSink(() => {
|
|
339
340
|
stdioTransport.send(createNotification(Methods.SettingsChanged, {}));
|
|
340
341
|
});
|
|
342
|
+
// Conversational Plugin/Skill/MCP installs happen inside this worker. Tell the
|
|
343
|
+
// mounted Desktop renderer to refresh catalogs and push a fresh settings patch
|
|
344
|
+
// back to all live sessions, matching installs initiated from Extensions.
|
|
345
|
+
setCapabilityChangedSink(() => {
|
|
346
|
+
stdioTransport.send(createNotification(Methods.SettingsChanged, {}));
|
|
347
|
+
});
|
|
341
348
|
// Disk-only active-goal reader for agent/goalGet. In worker mode there is no
|
|
342
349
|
// legacyEngine, and reopening a session after restart doesn't create a live
|
|
343
350
|
// ChatSession (that only happens on a send), so chatManager.get() misses. This
|
|
@@ -47,7 +47,21 @@ export declare class CredentialStore {
|
|
|
47
47
|
*/
|
|
48
48
|
private decryptSecret;
|
|
49
49
|
private read;
|
|
50
|
+
/**
|
|
51
|
+
* Read the store, reporting whether the on-disk state was fully understood.
|
|
52
|
+
*
|
|
53
|
+
* `readable: false` means "there is a file here but this build could not
|
|
54
|
+
* parse it" — NOT "there are no credentials". The distinction matters because
|
|
55
|
+
* mutate() commits whatever this returns: treating an unreadable file as an
|
|
56
|
+
* empty list turns any later save() into a wipe of every stored credential.
|
|
57
|
+
*
|
|
58
|
+
* `unknown` carries entries this build's schema rejects (for example a
|
|
59
|
+
* credential type added by a newer version). They are kept verbatim so a
|
|
60
|
+
* round-trip through an older build preserves rather than deletes them.
|
|
61
|
+
*/
|
|
62
|
+
private readGuarded;
|
|
50
63
|
private write;
|
|
64
|
+
private mutate;
|
|
51
65
|
/** Upsert by id within a scope. */
|
|
52
66
|
save(scope: CredentialScope, cred: Credential): void;
|
|
53
67
|
/**
|