@henryqw/pi-open-in 0.2.2 → 0.2.3

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/README.md CHANGED
@@ -27,6 +27,8 @@ pi install npm:@henryqw/pi-open-in
27
27
 
28
28
  The command is split on whitespace; tokens cannot contain spaces (no quoting). Use a wrapper script for executables in spaced paths.
29
29
 
30
+ A missing config file falls back to `code`. An existing file must be a JSON object with exactly one non-empty string `command` property; otherwise `/open` fails with a visible error. The file is never rewritten by this extension except via `/set-open-in`. When the config is invalid, no open URI is offered.
31
+
30
32
  ## Remove
31
33
 
32
34
  ```bash
@@ -10,21 +10,36 @@ import {
10
10
  const DEFAULT_COMMAND = "code";
11
11
  const configPath = () => join(getAgentDir(), "config", "pi-open-in.json");
12
12
 
13
- function configuredCommand(): string {
13
+ function loadCommand(): string {
14
+ let raw: string;
14
15
  try {
15
- const config: unknown = JSON.parse(readFileSync(configPath(), "utf8"));
16
- if (config && typeof config === "object" && !Array.isArray(config)) {
17
- const command = (config as { command?: unknown }).command;
18
- if (typeof command === "string" && command.trim()) return command.trim();
19
- }
16
+ raw = readFileSync(configPath(), "utf8");
17
+ } catch (error) {
18
+ // Only a missing file falls back to the default command.
19
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return DEFAULT_COMMAND;
20
+ throw error;
21
+ }
22
+ let config: unknown;
23
+ try {
24
+ config = JSON.parse(raw);
20
25
  } catch {
21
- // Missing or malformed config uses default command.
26
+ throw new Error(`Invalid ${configPath()}: not valid JSON`);
27
+ }
28
+ const isObject = (value: unknown): value is Record<string, unknown> =>
29
+ typeof value === "object" && value !== null && !Array.isArray(value);
30
+ if (!isObject(config) || Object.keys(config).length !== 1 || typeof config.command !== "string" || !config.command.trim()) {
31
+ throw new Error(`Invalid ${configPath()}: expected exactly one non-empty string "command" property`);
22
32
  }
23
- return DEFAULT_COMMAND;
33
+ return config.command.trim();
24
34
  }
25
35
 
26
36
  export function configuredOpenUri(path: string): string | undefined {
27
- if (configuredCommand() !== "code") return undefined;
37
+ try {
38
+ if (loadCommand() !== "code") return undefined;
39
+ } catch {
40
+ // Invalid config must not break footer render; just omit the URI.
41
+ return undefined;
42
+ }
28
43
  if (path.startsWith("\\\\")) {
29
44
  const [host, ...parts] = path.slice(2).split("\\");
30
45
  const uri = new URL(`file://${host}`);
@@ -47,7 +62,7 @@ export default function openInExtension(pi: ExtensionAPI): void {
47
62
  pi.registerCommand("open", {
48
63
  description: "Open the current path with the configured command",
49
64
  handler: async (_args, ctx) => {
50
- const [executable, ...args] = configuredCommand().split(/\s+/);
65
+ const [executable, ...args] = loadCommand().split(/\s+/);
51
66
  const result = await pi.exec(executable, [...args, ctx.cwd]);
52
67
  if (result.code !== 0) {
53
68
  throw new Error(`Open command failed: ${result.stderr.trim() || `exit code ${result.code}`}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henryqw/pi-open-in",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Open the current Pi working directory with a configurable command.",
5
5
  "keywords": [
6
6
  "pi-package",