@syengup/friday-channel-next 0.1.28 → 0.1.29

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.
@@ -1,10 +1,29 @@
1
1
  /** Install source from the OpenClaw config `plugins.installs[<id>].source`. */
2
2
  export type InstallSource = "npm" | "path" | "archive" | "clawhub" | "git" | "marketplace" | "unknown";
3
3
  /**
4
- * Read the install source for this plugin from the live config snapshot.
5
- * Returns "unknown" when the record can't be resolved (e.g. runtime not captured).
6
- * Only "npm" is auto-upgradable; "path" means a dev (load.paths) install which must
7
- * never be npm-upgraded (would duplicate-install and break agent media sends).
4
+ * Infer the install source from the loaded plugin's filesystem path (`api.source`).
5
+ *
6
+ * OpenClaw copies non-dev installs (npm/archive/clawhub/git) into
7
+ * `~/.openclaw/npm/projects/<hash>/node_modules/...`, whereas a dev install
8
+ * (`load.paths` / `plugins install --link`) is loaded directly from the source
9
+ * checkout. For this plugin's purposes the only distinction that matters is
10
+ * npm-managed (auto-upgradable) vs dev (must never npm-upgrade — see
11
+ * dev-no-duplicate-plugin-install), so anything under the managed projects dir
12
+ * is treated as "npm".
13
+ */
14
+ export declare function classifyInstallSourceFromLoadedPath(loadedPath: string | null | undefined): InstallSource;
15
+ /**
16
+ * Resolve the install source for this plugin (npm vs dev path).
17
+ * Returns "unknown" when it can't be resolved (e.g. runtime not captured).
18
+ * Only "npm" is auto-upgradable; "path" means a dev (load.paths / --link) install
19
+ * which must never be npm-upgraded (would duplicate-install and break agent media sends).
20
+ *
21
+ * Resolution order:
22
+ * 1. The explicit `plugins.installs[<id>].source` config record — present on older
23
+ * OpenClaw builds that surface install records in the runtime config snapshot.
24
+ * 2. Fallback: OpenClaw 2026.6.x moved install records out of the config snapshot
25
+ * into a separate registry (~/.openclaw/state.db), leaving `plugins.installs`
26
+ * unset — so infer from the loaded plugin path (`api.source`) instead.
8
27
  */
9
28
  export declare function getInstallSource(): InstallSource;
10
29
  /** Compare dotted numeric versions. Returns true if `a` is strictly greater than `b`. */
@@ -6,10 +6,33 @@
6
6
  import { getUpgradeRuntime } from "./upgrade-runtime.js";
7
7
  import { PLUGIN_ID, PLUGIN_PACKAGE_NAME } from "./version.js";
8
8
  /**
9
- * Read the install source for this plugin from the live config snapshot.
10
- * Returns "unknown" when the record can't be resolved (e.g. runtime not captured).
11
- * Only "npm" is auto-upgradable; "path" means a dev (load.paths) install which must
12
- * never be npm-upgraded (would duplicate-install and break agent media sends).
9
+ * Infer the install source from the loaded plugin's filesystem path (`api.source`).
10
+ *
11
+ * OpenClaw copies non-dev installs (npm/archive/clawhub/git) into
12
+ * `~/.openclaw/npm/projects/<hash>/node_modules/...`, whereas a dev install
13
+ * (`load.paths` / `plugins install --link`) is loaded directly from the source
14
+ * checkout. For this plugin's purposes the only distinction that matters is
15
+ * npm-managed (auto-upgradable) vs dev (must never npm-upgrade — see
16
+ * dev-no-duplicate-plugin-install), so anything under the managed projects dir
17
+ * is treated as "npm".
18
+ */
19
+ export function classifyInstallSourceFromLoadedPath(loadedPath) {
20
+ if (!loadedPath)
21
+ return "unknown";
22
+ return loadedPath.includes("/.openclaw/npm/projects/") ? "npm" : "path";
23
+ }
24
+ /**
25
+ * Resolve the install source for this plugin (npm vs dev path).
26
+ * Returns "unknown" when it can't be resolved (e.g. runtime not captured).
27
+ * Only "npm" is auto-upgradable; "path" means a dev (load.paths / --link) install
28
+ * which must never be npm-upgraded (would duplicate-install and break agent media sends).
29
+ *
30
+ * Resolution order:
31
+ * 1. The explicit `plugins.installs[<id>].source` config record — present on older
32
+ * OpenClaw builds that surface install records in the runtime config snapshot.
33
+ * 2. Fallback: OpenClaw 2026.6.x moved install records out of the config snapshot
34
+ * into a separate registry (~/.openclaw/state.db), leaving `plugins.installs`
35
+ * unset — so infer from the loaded plugin path (`api.source`) instead.
13
36
  */
14
37
  export function getInstallSource() {
15
38
  const rt = getUpgradeRuntime();
@@ -26,11 +49,11 @@ export function getInstallSource() {
26
49
  source === "marketplace") {
27
50
  return source;
28
51
  }
29
- return "unknown";
30
52
  }
31
53
  catch {
32
- return "unknown";
54
+ // fall through to the path-based heuristic
33
55
  }
56
+ return classifyInstallSourceFromLoadedPath(rt.pluginSource);
34
57
  }
35
58
  /** Compare dotted numeric versions. Returns true if `a` is strictly greater than `b`. */
36
59
  export function semverGreater(a, b) {
@@ -32,6 +32,12 @@ export type UpgradeRuntime = {
32
32
  afterWrite: ConfigAfterWrite;
33
33
  mutate: (draft: unknown) => unknown | void;
34
34
  }) => Promise<unknown>;
35
+ /**
36
+ * Filesystem path of THIS loaded plugin (`api.source`). Used to infer the install
37
+ * source (npm vs dev) on OpenClaw builds (2026.6.x+) that no longer surface
38
+ * `plugins.installs` in the config snapshot. See `getInstallSource`.
39
+ */
40
+ pluginSource: string | undefined;
35
41
  };
36
42
  export declare function setUpgradeRuntime(api: OpenClawPluginApi): void;
37
43
  export declare function getUpgradeRuntime(): UpgradeRuntime | null;
@@ -16,6 +16,7 @@ export function setUpgradeRuntime(api) {
16
16
  throw new Error("runtime.config.mutateConfigFile unavailable");
17
17
  return mutate(params);
18
18
  },
19
+ pluginSource: typeof api.source === "string" ? api.source : undefined,
19
20
  };
20
21
  }
21
22
  export function getUpgradeRuntime() {
@@ -10,7 +10,7 @@
10
10
  import { readFileSync } from "node:fs";
11
11
  import { fileURLToPath } from "node:url";
12
12
  /** Keep in sync with package.json "version" as a last-resort fallback. */
13
- const FALLBACK_VERSION = "0.1.28";
13
+ const FALLBACK_VERSION = "0.1.29";
14
14
  function resolvePluginVersion() {
15
15
  // dist layout: <root>/dist/src/version.js → ../../package.json = <root>/package.json
16
16
  // source layout (vitest/jiti): <root>/src/version.ts → ../package.json = <root>/package.json
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syengup/friday-channel-next",
3
- "version": "0.1.28",
3
+ "version": "0.1.29",
4
4
  "description": "OpenClaw Friday Next Apple channel plugin",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,5 +1,35 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { semverGreater } from "./plugin-install-info.js";
2
+ import { classifyInstallSourceFromLoadedPath, semverGreater } from "./plugin-install-info.js";
3
+
4
+ describe("classifyInstallSourceFromLoadedPath", () => {
5
+ it("treats paths under the managed npm projects dir as npm", () => {
6
+ expect(
7
+ classifyInstallSourceFromLoadedPath(
8
+ "/Users/me/.openclaw/npm/projects/syengup-friday-channel-next-ef89e139a1/node_modules/@syengup/friday-channel-next/dist/index.js",
9
+ ),
10
+ ).toBe("npm");
11
+ // install-path form (no /dist/index.js suffix) also resolves to npm
12
+ expect(
13
+ classifyInstallSourceFromLoadedPath(
14
+ "/Users/me/.openclaw/npm/projects/syengup-friday-channel-next-ef89e139a1/node_modules/@syengup/friday-channel-next",
15
+ ),
16
+ ).toBe("npm");
17
+ });
18
+
19
+ it("treats a dev/link checkout path as path", () => {
20
+ expect(
21
+ classifyInstallSourceFromLoadedPath(
22
+ "/Users/me/Documents/Project/Friday-Next/openclaw-fridaynext-channel/dist/index.js",
23
+ ),
24
+ ).toBe("path");
25
+ });
26
+
27
+ it("returns unknown for missing/empty input", () => {
28
+ expect(classifyInstallSourceFromLoadedPath(null)).toBe("unknown");
29
+ expect(classifyInstallSourceFromLoadedPath(undefined)).toBe("unknown");
30
+ expect(classifyInstallSourceFromLoadedPath("")).toBe("unknown");
31
+ });
32
+ });
3
33
 
4
34
  describe("semverGreater", () => {
5
35
  it("returns true when a is a higher patch/minor/major", () => {
@@ -10,10 +10,33 @@ import { PLUGIN_ID, PLUGIN_PACKAGE_NAME } from "./version.js";
10
10
  export type InstallSource = "npm" | "path" | "archive" | "clawhub" | "git" | "marketplace" | "unknown";
11
11
 
12
12
  /**
13
- * Read the install source for this plugin from the live config snapshot.
14
- * Returns "unknown" when the record can't be resolved (e.g. runtime not captured).
15
- * Only "npm" is auto-upgradable; "path" means a dev (load.paths) install which must
16
- * never be npm-upgraded (would duplicate-install and break agent media sends).
13
+ * Infer the install source from the loaded plugin's filesystem path (`api.source`).
14
+ *
15
+ * OpenClaw copies non-dev installs (npm/archive/clawhub/git) into
16
+ * `~/.openclaw/npm/projects/<hash>/node_modules/...`, whereas a dev install
17
+ * (`load.paths` / `plugins install --link`) is loaded directly from the source
18
+ * checkout. For this plugin's purposes the only distinction that matters is
19
+ * npm-managed (auto-upgradable) vs dev (must never npm-upgrade — see
20
+ * dev-no-duplicate-plugin-install), so anything under the managed projects dir
21
+ * is treated as "npm".
22
+ */
23
+ export function classifyInstallSourceFromLoadedPath(loadedPath: string | null | undefined): InstallSource {
24
+ if (!loadedPath) return "unknown";
25
+ return loadedPath.includes("/.openclaw/npm/projects/") ? "npm" : "path";
26
+ }
27
+
28
+ /**
29
+ * Resolve the install source for this plugin (npm vs dev path).
30
+ * Returns "unknown" when it can't be resolved (e.g. runtime not captured).
31
+ * Only "npm" is auto-upgradable; "path" means a dev (load.paths / --link) install
32
+ * which must never be npm-upgraded (would duplicate-install and break agent media sends).
33
+ *
34
+ * Resolution order:
35
+ * 1. The explicit `plugins.installs[<id>].source` config record — present on older
36
+ * OpenClaw builds that surface install records in the runtime config snapshot.
37
+ * 2. Fallback: OpenClaw 2026.6.x moved install records out of the config snapshot
38
+ * into a separate registry (~/.openclaw/state.db), leaving `plugins.installs`
39
+ * unset — so infer from the loaded plugin path (`api.source`) instead.
17
40
  */
18
41
  export function getInstallSource(): InstallSource {
19
42
  const rt = getUpgradeRuntime();
@@ -33,10 +56,10 @@ export function getInstallSource(): InstallSource {
33
56
  ) {
34
57
  return source;
35
58
  }
36
- return "unknown";
37
59
  } catch {
38
- return "unknown";
60
+ // fall through to the path-based heuristic
39
61
  }
62
+ return classifyInstallSourceFromLoadedPath(rt.pluginSource);
40
63
  }
41
64
 
42
65
  /** Compare dotted numeric versions. Returns true if `a` is strictly greater than `b`. */
@@ -30,6 +30,12 @@ export type UpgradeRuntime = {
30
30
  afterWrite: ConfigAfterWrite;
31
31
  mutate: (draft: unknown) => unknown | void;
32
32
  }) => Promise<unknown>;
33
+ /**
34
+ * Filesystem path of THIS loaded plugin (`api.source`). Used to infer the install
35
+ * source (npm vs dev) on OpenClaw builds (2026.6.x+) that no longer surface
36
+ * `plugins.installs` in the config snapshot. See `getInstallSource`.
37
+ */
38
+ pluginSource: string | undefined;
33
39
  };
34
40
 
35
41
  let upgradeRuntime: UpgradeRuntime | null = null;
@@ -56,6 +62,7 @@ export function setUpgradeRuntime(api: OpenClawPluginApi): void {
56
62
  if (!mutate) throw new Error("runtime.config.mutateConfigFile unavailable");
57
63
  return mutate(params);
58
64
  },
65
+ pluginSource: typeof api.source === "string" ? api.source : undefined,
59
66
  };
60
67
  }
61
68
 
package/src/version.ts CHANGED
@@ -11,7 +11,7 @@ import { readFileSync } from "node:fs";
11
11
  import { fileURLToPath } from "node:url";
12
12
 
13
13
  /** Keep in sync with package.json "version" as a last-resort fallback. */
14
- const FALLBACK_VERSION = "0.1.28";
14
+ const FALLBACK_VERSION = "0.1.29";
15
15
 
16
16
  function resolvePluginVersion(): string {
17
17
  // dist layout: <root>/dist/src/version.js → ../../package.json = <root>/package.json