@cjhyy/code-shell-core 0.8.6 → 0.8.8

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.
@@ -432,9 +432,10 @@ function isSkillTreeResource(resolved, skillsRoot) {
432
432
  *
433
433
  * Keep the exception narrow:
434
434
  * - read-only (the caller checks the operation);
435
- * - user Skills, or an installed plugin recorded in the V2 registry;
435
+ * - user Skills, an installed plugin recorded in the V2 registry, or a
436
+ * declared Skill in the installed Panel App registry;
436
437
  * - plugin installs must realpath beneath the managed plugin cache;
437
- * - the target must remain inside a directory containing a real SKILL.md.
438
+ * - the target must remain inside the registered Skill directory.
438
439
  */
439
440
  function isRegisteredSkillResourceRead(resolved) {
440
441
  let codeShellRoot;
@@ -448,6 +449,8 @@ function isRegisteredSkillResourceRead(resolved) {
448
449
  return false;
449
450
  if (isSkillTreeResource(resolved, join(codeShellRoot, "skills")))
450
451
  return true;
452
+ if (isInstalledPanelAppSkillResourceRead(resolved, codeShellRoot))
453
+ return true;
451
454
  let cacheRoot;
452
455
  try {
453
456
  cacheRoot = realpathSync(join(codeShellRoot, "plugins", "cache"));
@@ -475,6 +478,84 @@ function isRegisteredSkillResourceRead(resolved) {
475
478
  }
476
479
  return false;
477
480
  }
481
+ /**
482
+ * Panel App Skills live under the otherwise-sensitive
483
+ * `~/.code-shell/panel-apps` tree. Installation already reviews and copies the
484
+ * package, and the Skill scanner exposes only entries declared by the app
485
+ * manifest. Mirror that exact boundary here so reading a Skill reference does
486
+ * not trigger a second approval prompt.
487
+ *
488
+ * Registry, app root, manifest, declared SKILL.md and target are all
489
+ * realpathed and containment-checked. This deliberately does not trust an
490
+ * undeclared Skill directory or a symlink escaping the installed app.
491
+ */
492
+ function isInstalledPanelAppSkillResourceRead(resolved, codeShellRoot) {
493
+ let appsRoot;
494
+ let registryPath;
495
+ try {
496
+ appsRoot = realpathSync(join(codeShellRoot, "panel-apps"));
497
+ if (!isInsideDir(appsRoot, codeShellRoot))
498
+ return false;
499
+ registryPath = realpathSync(join(appsRoot, "installed.json"));
500
+ if (!isInsideDir(registryPath, appsRoot))
501
+ return false;
502
+ }
503
+ catch {
504
+ return false;
505
+ }
506
+ let registry;
507
+ try {
508
+ registry = JSON.parse(readFileSync(registryPath, "utf-8"));
509
+ }
510
+ catch {
511
+ return false;
512
+ }
513
+ if (registry.version !== 1 || !Array.isArray(registry.apps))
514
+ return false;
515
+ for (const rawEntry of registry.apps) {
516
+ if (!rawEntry || typeof rawEntry !== "object")
517
+ continue;
518
+ const id = rawEntry.id;
519
+ if (typeof id !== "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(id))
520
+ continue;
521
+ try {
522
+ const appRoot = realpathSync(join(appsRoot, id));
523
+ if (appRoot === appsRoot || !isInsideDir(appRoot, appsRoot))
524
+ continue;
525
+ const manifestPath = realpathSync(join(appRoot, ".codeshell-panel", "panel.json"));
526
+ if (!isInsideDir(manifestPath, appRoot) || !statSync(manifestPath).isFile())
527
+ continue;
528
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
529
+ if (manifest.schemaVersion !== 2 ||
530
+ manifest.id !== id ||
531
+ !Array.isArray(manifest.agent?.skills)) {
532
+ continue;
533
+ }
534
+ for (const skillEntry of manifest.agent.skills) {
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
+ }
552
+ }
553
+ catch {
554
+ // A stale, malformed, or tampered Panel App entry grants no read access.
555
+ }
556
+ }
557
+ return false;
558
+ }
478
559
  /**
479
560
  * Returns the matching sensitive-dir entry (with the user's home prefix) if
480
561
  * `resolved` lives underneath any sensitive directory, else undefined.
@@ -564,9 +645,7 @@ export function classifyPath(rawPath, opts) {
564
645
  // files through the ordinary Read tool as well. A credential-shaped basename
565
646
  // (.env, token.txt, key files, ...) deliberately keeps the sensitive-file
566
647
  // gate even inside a Skill tree.
567
- if (opts.operation === "read" &&
568
- !sensitiveFile &&
569
- isRegisteredSkillResourceRead(resolved)) {
648
+ if (opts.operation === "read" && !sensitiveFile && isRegisteredSkillResourceRead(resolved)) {
570
649
  return {
571
650
  decision: "allow",
572
651
  reason: "registered Skill resource read",
package/dist/types.d.ts CHANGED
@@ -145,7 +145,7 @@ export interface RegisteredTool {
145
145
  */
146
146
  timeoutMs?: number;
147
147
  }
148
- export type TranscriptEventType = "message" | "tool_use" | "tool_result" | "summary" | "context_transfer" | "content_replace" | "file_history" | "plan_operation" | "session_meta" | "subagent" | "external_file_changes" | "turn_boundary" | "run_result" | "goal_progress" | "turn_stopped" | "error";
148
+ export type TranscriptEventType = "message" | "tool_use" | "tool_result" | "summary" | "context_transfer" | "range_archive" | "content_replace" | "file_history" | "plan_operation" | "session_meta" | "subagent" | "external_file_changes" | "turn_boundary" | "run_result" | "goal_progress" | "turn_stopped" | "error";
149
149
  export interface TranscriptEvent {
150
150
  id: string;
151
151
  type: TranscriptEventType;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cjhyy/code-shell-core",
3
- "version": "0.8.6",
3
+ "version": "0.8.8",
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",