@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
|
@@ -70,7 +70,9 @@ export async function skillTool(args, ctx) {
|
|
|
70
70
|
});
|
|
71
71
|
const found = skills.find((s) => s.name === skillName);
|
|
72
72
|
if (!found) {
|
|
73
|
-
return `Skill "${skillName}" not found. Run /skills to list available skills
|
|
73
|
+
return (`Skill "${skillName}" not found. Run /skills to list available skills. ` +
|
|
74
|
+
"If the user asked to add it and you know its trusted GitHub source, use " +
|
|
75
|
+
"InstallCapability with kind='skill' instead of constructing a shell command.");
|
|
74
76
|
}
|
|
75
77
|
const skillDir = dirname(found.filePath);
|
|
76
78
|
const body = found.content
|
|
@@ -502,6 +502,7 @@ export class ToolExecutor {
|
|
|
502
502
|
toolCallId: call.id,
|
|
503
503
|
result: observerResult,
|
|
504
504
|
error: result.error,
|
|
505
|
+
isError: result.isError,
|
|
505
506
|
signal: this.signal ?? this.toolCtx?.signal,
|
|
506
507
|
});
|
|
507
508
|
// 5. Post-tool-use hook (after execution, can observe/modify result)
|
|
@@ -449,8 +449,6 @@ function isRegisteredSkillResourceRead(resolved) {
|
|
|
449
449
|
return false;
|
|
450
450
|
if (isSkillTreeResource(resolved, join(codeShellRoot, "skills")))
|
|
451
451
|
return true;
|
|
452
|
-
if (isInstalledPanelAppSkillResourceRead(resolved, codeShellRoot))
|
|
453
|
-
return true;
|
|
454
452
|
let cacheRoot;
|
|
455
453
|
try {
|
|
456
454
|
cacheRoot = realpathSync(join(codeShellRoot, "plugins", "cache"));
|
|
@@ -479,20 +477,26 @@ function isRegisteredSkillResourceRead(resolved) {
|
|
|
479
477
|
return false;
|
|
480
478
|
}
|
|
481
479
|
/**
|
|
482
|
-
* Panel
|
|
483
|
-
* `~/.code-shell/panel-apps` tree.
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
*
|
|
480
|
+
* Installed Panel Apps live under the otherwise-sensitive
|
|
481
|
+
* `~/.code-shell/panel-apps` tree. Their package contents are reviewed and
|
|
482
|
+
* copied by the installer, so ordinary source, manifests, assets and declared
|
|
483
|
+
* Agent resources should not require a second approval merely because their
|
|
484
|
+
* parent directory is `~/.code-shell`.
|
|
487
485
|
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
486
|
+
* Keep the exception read-only at the call site and require a valid installed
|
|
487
|
+
* registry entry plus a matching V2 manifest. Registry, app root, manifest and
|
|
488
|
+
* target are all realpathed and containment-checked. Credential-shaped files
|
|
489
|
+
* are still caught by `SENSITIVE_FILE_PATTERNS` before this exception, and a
|
|
490
|
+
* symlink escaping the installed package never inherits its read authority.
|
|
491
491
|
*/
|
|
492
|
-
function
|
|
492
|
+
function isInstalledPanelAppResourceRead(resolved) {
|
|
493
|
+
let codeShellRoot;
|
|
493
494
|
let appsRoot;
|
|
494
495
|
let registryPath;
|
|
495
496
|
try {
|
|
497
|
+
codeShellRoot = realpathSync(join(configuredUserHome(), ".code-shell"));
|
|
498
|
+
if (!isInsideDir(resolved, codeShellRoot))
|
|
499
|
+
return false;
|
|
496
500
|
appsRoot = realpathSync(join(codeShellRoot, "panel-apps"));
|
|
497
501
|
if (!isInsideDir(appsRoot, codeShellRoot))
|
|
498
502
|
return false;
|
|
@@ -526,29 +530,10 @@ function isInstalledPanelAppSkillResourceRead(resolved, codeShellRoot) {
|
|
|
526
530
|
if (!isInsideDir(manifestPath, appRoot) || !statSync(manifestPath).isFile())
|
|
527
531
|
continue;
|
|
528
532
|
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
529
|
-
if (manifest.schemaVersion !== 2 ||
|
|
530
|
-
manifest.id !== id ||
|
|
531
|
-
!Array.isArray(manifest.agent?.skills)) {
|
|
533
|
+
if (manifest.schemaVersion !== 2 || manifest.id !== id)
|
|
532
534
|
continue;
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
if (typeof skillEntry !== "string")
|
|
536
|
-
continue;
|
|
537
|
-
const segments = skillEntry.split("/");
|
|
538
|
-
if (segments.length !== 4 ||
|
|
539
|
-
segments[0] !== "agent" ||
|
|
540
|
-
segments[1] !== "skills" ||
|
|
541
|
-
!/^[a-z][a-z0-9-]{0,63}$/.test(segments[2] ?? "") ||
|
|
542
|
-
segments[3] !== "SKILL.md") {
|
|
543
|
-
continue;
|
|
544
|
-
}
|
|
545
|
-
const skillManifest = realpathSync(join(appRoot, ...segments));
|
|
546
|
-
if (!isInsideDir(skillManifest, appRoot) || !statSync(skillManifest).isFile())
|
|
547
|
-
continue;
|
|
548
|
-
const skillRoot = dirname(skillManifest);
|
|
549
|
-
if (isInsideDir(resolved, skillRoot))
|
|
550
|
-
return true;
|
|
551
|
-
}
|
|
535
|
+
if (isInsideDir(resolved, appRoot))
|
|
536
|
+
return true;
|
|
552
537
|
}
|
|
553
538
|
catch {
|
|
554
539
|
// A stale, malformed, or tampered Panel App entry grants no read access.
|
|
@@ -652,6 +637,13 @@ export function classifyPath(rawPath, opts) {
|
|
|
652
637
|
resolvedPath: resolved,
|
|
653
638
|
};
|
|
654
639
|
}
|
|
640
|
+
if (opts.operation === "read" && !sensitiveFile && isInstalledPanelAppResourceRead(resolved)) {
|
|
641
|
+
return {
|
|
642
|
+
decision: "allow",
|
|
643
|
+
reason: "installed Panel App resource read",
|
|
644
|
+
resolvedPath: resolved,
|
|
645
|
+
};
|
|
646
|
+
}
|
|
655
647
|
// Sensitive: write is always denied, read always asks. Workspace placement
|
|
656
648
|
// doesn't soften the rule — an `.env` in the project still asks on read.
|
|
657
649
|
if (sensitiveLabel) {
|
|
@@ -57,6 +57,11 @@ export class ToolRegistry {
|
|
|
57
57
|
const fork = new ToolRegistry({ builtinTools: [] });
|
|
58
58
|
fork.tools = new Map(this.tools);
|
|
59
59
|
fork.builtinExecutors = new Map(this.builtinExecutors);
|
|
60
|
+
// Availability is part of the executable tool contract, not just prompt
|
|
61
|
+
// decoration. Engine instances run against this fork, so dropping the
|
|
62
|
+
// guards here lets a model invoke a tool that was correctly hidden from
|
|
63
|
+
// its current turn (for example GatewayReply in a desktop Mimi chat).
|
|
64
|
+
fork.availabilityGuards = new Map(this.availabilityGuards);
|
|
60
65
|
return fork;
|
|
61
66
|
}
|
|
62
67
|
unregisterTool(name) {
|
package/dist/utils/file-mutex.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* `<dir>.lock`), matching CronStore. Locking the directory rather than the file
|
|
20
20
|
* means a writer that creates the file for the first time is still serialized.
|
|
21
21
|
*/
|
|
22
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
22
|
+
import { closeSync, constants, existsSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
23
23
|
import { dirname } from "node:path";
|
|
24
24
|
import { randomUUID } from "node:crypto";
|
|
25
25
|
import { lockSync } from "./lockfile.js";
|
|
@@ -39,6 +39,7 @@ const LOCK_STALE_MS = 10_000;
|
|
|
39
39
|
/** Backoff bounds for the retry loop (see acquireLockOn). */
|
|
40
40
|
const LOCK_RETRY_MIN_MS = 5;
|
|
41
41
|
const LOCK_RETRY_MAX_MS = 50;
|
|
42
|
+
const DEFAULT_JSON_FILE_MAX_BYTES = 16 * 1024 * 1024;
|
|
42
43
|
// Atomics.wait only ever reads slot 0, which stays 0, so one shared buffer is
|
|
43
44
|
// safe and avoids allocating per retry.
|
|
44
45
|
const SLEEP_SIGNAL = new Int32Array(new SharedArrayBuffer(4));
|
|
@@ -123,19 +124,43 @@ export function writeFileAtomic(file, contents, mode) {
|
|
|
123
124
|
* skips the write entirely (no-op mutations should not rewrite the file).
|
|
124
125
|
*/
|
|
125
126
|
export function mutateJsonFile(file, options) {
|
|
127
|
+
const maxBytes = options.maxBytes ?? DEFAULT_JSON_FILE_MAX_BYTES;
|
|
128
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) {
|
|
129
|
+
throw new Error("JSON state maxBytes must be a positive safe integer");
|
|
130
|
+
}
|
|
126
131
|
const release = acquireFileLock(file);
|
|
127
132
|
try {
|
|
128
133
|
let raw;
|
|
129
134
|
try {
|
|
130
|
-
|
|
135
|
+
const pathInfo = lstatSync(file);
|
|
136
|
+
if (pathInfo.isSymbolicLink() || !pathInfo.isFile() || pathInfo.size > maxBytes) {
|
|
137
|
+
throw new Error(`JSON state is not a bounded regular file: ${file}`);
|
|
138
|
+
}
|
|
139
|
+
const descriptor = openSync(file, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
|
|
140
|
+
try {
|
|
141
|
+
const opened = fstatSync(descriptor);
|
|
142
|
+
if (!opened.isFile() || opened.size > maxBytes) {
|
|
143
|
+
throw new Error(`JSON state is not a bounded regular file: ${file}`);
|
|
144
|
+
}
|
|
145
|
+
raw = readFileSync(descriptor, "utf-8");
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
closeSync(descriptor);
|
|
149
|
+
}
|
|
131
150
|
}
|
|
132
|
-
catch {
|
|
151
|
+
catch (error) {
|
|
152
|
+
if (error.code !== "ENOENT")
|
|
153
|
+
throw error;
|
|
133
154
|
raw = undefined;
|
|
134
155
|
}
|
|
135
156
|
const current = options.parse(raw);
|
|
136
157
|
const { value, result } = options.mutation(current);
|
|
137
158
|
if (value !== undefined) {
|
|
138
|
-
|
|
159
|
+
const serialized = options.serialize(value);
|
|
160
|
+
if (Buffer.byteLength(serialized, "utf8") > maxBytes) {
|
|
161
|
+
throw new Error(`JSON state exceeds ${maxBytes} bytes`);
|
|
162
|
+
}
|
|
163
|
+
writeFileAtomic(file, serialized, options.mode);
|
|
139
164
|
}
|
|
140
165
|
return result;
|
|
141
166
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cjhyy/code-shell-core",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.10",
|
|
4
4
|
"description": "Core engine for code-shell — agent orchestration, tool execution, hooks, protocol. UI-agnostic.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
],
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "bun run clean && tsc -p tsconfig.json && bun run copy-assets",
|
|
41
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
41
42
|
"copy-assets": "node ../../scripts/copy-assets.mjs dist/prompt/sections src/prompt/sections/*.md && node ../../scripts/copy-assets.mjs dist/data src/data/*.json",
|
|
42
43
|
"dev": "bun run copy-assets && tsc -p tsconfig.json --watch --preserveWatchOutput",
|
|
43
44
|
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\""
|