dsh-packycode-api 1.0.1 → 1.0.4

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/lib/index.js CHANGED
@@ -8,6 +8,8 @@ import { homedir, tmpdir } from "node:os";
8
8
  import { spawn } from "node:child_process";
9
9
  import { existsSync, readFileSync, readdirSync } from "node:fs";
10
10
  import { credentialKey } from "@deepseek-ai/dsh-credentials";
11
+ import { readFile, readdir, realpath } from "node:fs/promises";
12
+ import { fileURLToPath } from "node:url";
11
13
  import { createProvider } from "@earendil-works/pi-ai";
12
14
  import { anthropicMessagesApi } from "@earendil-works/pi-ai/api/anthropic-messages.lazy";
13
15
  import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
@@ -67,7 +69,7 @@ const DEFAULT_REASONING_LEVEL = "high";
67
69
  * @module dsh-packycode-api/balance
68
70
  */
69
71
  const record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
70
- const clone = (value) => structuredClone(value);
72
+ const clone$1 = (value) => structuredClone(value);
71
73
  function number(value) {
72
74
  if (typeof value === "number" && Number.isFinite(value)) return value;
73
75
  if (typeof value === "string" && value.trim() !== "") {
@@ -274,7 +276,7 @@ function createBalanceReader({ listKeys, getHosts, fetchImpl = fetch, now = Date
274
276
  return Object.freeze({
275
277
  async read({ force = false, signal } = {}) {
276
278
  signal?.throwIfAborted();
277
- if (!force && cached !== void 0 && now() - cached.fetchedAt < ttlMs) return clone(cached);
279
+ if (!force && cached !== void 0 && now() - cached.fetchedAt < ttlMs) return clone$1(cached);
278
280
  const createPending = (requestSignal) => {
279
281
  const observedGeneration = generation;
280
282
  return load(requestSignal).then((value) => {
@@ -282,7 +284,7 @@ function createBalanceReader({ listKeys, getHosts, fetchImpl = fetch, now = Date
282
284
  return value;
283
285
  });
284
286
  };
285
- if (force) return clone(await createPending(signal));
287
+ if (force) return clone$1(await createPending(signal));
286
288
  const pending = inFlight ?? createPending();
287
289
  if (inFlight === void 0) {
288
290
  inFlight = pending;
@@ -291,7 +293,7 @@ function createBalanceReader({ listKeys, getHosts, fetchImpl = fetch, now = Date
291
293
  };
292
294
  pending.then(clear, clear);
293
295
  }
294
- return clone(await pending);
296
+ return clone$1(await pending);
295
297
  },
296
298
  invalidate() {
297
299
  generation += 1;
@@ -1819,6 +1821,20 @@ var PackyCodeKeyRing = class {
1819
1821
  };
1820
1822
  }));
1821
1823
  }
1824
+ async rename(id, label) {
1825
+ const name = text(label).trim().slice(0, 60);
1826
+ if (name === "") throw new Error("That PackyCode key name cannot be empty");
1827
+ return projection(await this.#write((state) => {
1828
+ if (!state.keys.some((candidate) => candidate.id === id)) throw new Error("Unknown PackyCode key");
1829
+ return {
1830
+ activeId: state.activeId,
1831
+ keys: state.keys.map((candidate) => candidate.id === id ? {
1832
+ ...candidate,
1833
+ label: name
1834
+ } : candidate)
1835
+ };
1836
+ }));
1837
+ }
1822
1838
  async remove(id) {
1823
1839
  return projection(await this.#write((state) => {
1824
1840
  const remaining = state.keys.filter((candidate) => candidate.id !== id);
@@ -1946,6 +1962,383 @@ function capClaudeMode(requested, ceiling) {
1946
1962
  return RANK[requested] > RANK[ceiling] ? ceiling : requested;
1947
1963
  }
1948
1964
  //#endregion
1965
+ //#region src/plugin-version.js
1966
+ /**
1967
+ * Version self-knowledge for the settings panel.
1968
+ *
1969
+ * The running version comes from this package. The latest version comes from
1970
+ * the npm registry. An in-place update is only offered for a registry install,
1971
+ * and it installs that exact version with the host's own pnpm — never through
1972
+ * `dsh plugin --profile desktop`, which the desktop app refuses. Registry
1973
+ * bodies and package-manager output stay host-side.
1974
+ *
1975
+ * @module dsh-packycode-api/plugin-version
1976
+ */
1977
+ const PACKAGE_NAME = "dsh-packycode-api";
1978
+ const NPM_REGISTRY_LATEST_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
1979
+ const DEFAULT_VERSION_TTL_MS = 5 * 6e4;
1980
+ const DEFAULT_VERSION_TIMEOUT_MS = 1e4;
1981
+ const DEFAULT_UPDATE_TIMEOUT_MS = 18e4;
1982
+ const clone = (value) => structuredClone(value);
1983
+ /**
1984
+ * Resolve fetch at call time. A sibling plugin may wrap `globalThis.fetch`
1985
+ * and later dismantle that wrapper; a binding captured during `apply()` would
1986
+ * keep the dead wrapper for the process lifetime.
1987
+ */
1988
+ function ambientFetch(input, init) {
1989
+ const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
1990
+ if (!headers.has("accept-encoding")) headers.set("accept-encoding", "identity");
1991
+ return globalThis.fetch(input, {
1992
+ ...init,
1993
+ headers
1994
+ });
1995
+ }
1996
+ /** One caller's cancellation must not abort the shared registry read. */
1997
+ function waitForSharedRequest(promise, signal) {
1998
+ if (signal === void 0) return promise;
1999
+ signal.throwIfAborted();
2000
+ return new Promise((resolve, reject) => {
2001
+ const onAbort = () => reject(signal.reason);
2002
+ signal.addEventListener("abort", onAbort, { once: true });
2003
+ promise.then((value) => {
2004
+ signal.removeEventListener("abort", onAbort);
2005
+ resolve(value);
2006
+ }, (error) => {
2007
+ signal.removeEventListener("abort", onAbort);
2008
+ reject(error);
2009
+ });
2010
+ });
2011
+ }
2012
+ /** Parse a strict `x.y.z[-pre][+build]` version; anything else is undefined. */
2013
+ function parseSemver(value) {
2014
+ if (typeof value !== "string") return void 0;
2015
+ const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/u.exec(value.trim());
2016
+ if (match === null) return void 0;
2017
+ return {
2018
+ major: Number(match[1]),
2019
+ minor: Number(match[2]),
2020
+ patch: Number(match[3]),
2021
+ pre: match[4]
2022
+ };
2023
+ }
2024
+ /** Compare two versions: -1/0/1, or undefined when either side is not semver. */
2025
+ function compareSemver(a, b) {
2026
+ const left = parseSemver(a);
2027
+ const right = parseSemver(b);
2028
+ if (left === void 0 || right === void 0) return void 0;
2029
+ for (const key of [
2030
+ "major",
2031
+ "minor",
2032
+ "patch"
2033
+ ]) if (left[key] !== right[key]) return left[key] < right[key] ? -1 : 1;
2034
+ if (left.pre === right.pre) return 0;
2035
+ if (left.pre === void 0) return 1;
2036
+ if (right.pre === void 0) return -1;
2037
+ return left.pre < right.pre ? -1 : 1;
2038
+ }
2039
+ /**
2040
+ * Classify how the running package reached the profile. Only registry specs
2041
+ * may be updated in place; local checkouts belong to the owner's iteration.
2042
+ */
2043
+ function classifySpec(spec) {
2044
+ if (typeof spec !== "string" || spec === "") return "unknown";
2045
+ if (/^(?:link|file):/u.test(spec)) return "link";
2046
+ if (/^(?:\d|[~^*])/u.test(spec)) return "npm";
2047
+ return "unknown";
2048
+ }
2049
+ const readManifest = async (path) => {
2050
+ try {
2051
+ const parsed = JSON.parse(await readFile(path, "utf8"));
2052
+ return parsed !== null && typeof parsed === "object" ? parsed : void 0;
2053
+ } catch {
2054
+ return;
2055
+ }
2056
+ };
2057
+ /**
2058
+ * A zero exit only proves the command ran. pnpm can skip a too-new version
2059
+ * and still exit zero, so the landed manifest is what makes the answer true.
2060
+ */
2061
+ async function confirmInstalled({ profileDir, expected, settleDelayMs, attempts = 3 }) {
2062
+ for (let attempt = 1; attempt <= attempts; attempt += 1) {
2063
+ if ((await readManifest(join(profileDir, "node_modules", "dsh-packycode-api", "package.json")))?.version === expected) return;
2064
+ if (attempt < attempts) await new Promise((resolve) => {
2065
+ setTimeout(resolve, settleDelayMs);
2066
+ });
2067
+ }
2068
+ throw new Error("PackyCode plugin update did not install the requested version");
2069
+ }
2070
+ /**
2071
+ * Find the profile that owns this running installation. pnpm symlinks resolve
2072
+ * to the same realpath on both sides. When no realpath matches, fall back to
2073
+ * the only profile that declares the dependency at all.
2074
+ */
2075
+ async function findInstall({ dshHome, ownPackageJsonUrl }) {
2076
+ let ownReal;
2077
+ try {
2078
+ ownReal = await realpath(fileURLToPath(ownPackageJsonUrl));
2079
+ } catch {
2080
+ ownReal = void 0;
2081
+ }
2082
+ let entries = [];
2083
+ try {
2084
+ entries = await readdir(join(dshHome, "profiles"), { withFileTypes: true });
2085
+ } catch {
2086
+ entries = [];
2087
+ }
2088
+ const declared = [];
2089
+ for (const entry of entries) {
2090
+ if (!entry.isDirectory()) continue;
2091
+ const profileDir = join(dshHome, "profiles", entry.name);
2092
+ const spec = (await readManifest(join(profileDir, "package.json")))?.dependencies?.[PACKAGE_NAME];
2093
+ if (typeof spec !== "string") continue;
2094
+ declared.push({
2095
+ profile: entry.name,
2096
+ spec
2097
+ });
2098
+ if (ownReal === void 0) continue;
2099
+ try {
2100
+ if (await realpath(join(profileDir, "node_modules", "dsh-packycode-api", "package.json")) === ownReal) return {
2101
+ kind: classifySpec(spec),
2102
+ profile: entry.name
2103
+ };
2104
+ } catch {}
2105
+ }
2106
+ if (declared.length === 1) return {
2107
+ kind: classifySpec(declared[0].spec),
2108
+ profile: declared[0].profile
2109
+ };
2110
+ return { kind: "unknown" };
2111
+ }
2112
+ const isExistingPath = (value) => typeof value === "string" && value !== "" && existsSync(value);
2113
+ /** Anchored to the file name so a directory that merely contains the word cannot match. */
2114
+ const PNPM_SCRIPT = /[\\/]pnpm\.(?:mjs|cjs|js)$/u;
2115
+ const dshRuntimeRoot = (value) => isExistingPath(value) && existsSync(join(value, "node_modules", "@deepseek-ai", "dsh", "package.json")) ? value : void 0;
2116
+ /**
2117
+ * Locate the package manager that owns this profile.
2118
+ *
2119
+ * `dsh plugin --profile desktop` is refused by the desktop app, so the update
2120
+ * installs with pnpm directly. The desktop host puts the bundled pnpm in its
2121
+ * own arguments; nothing is guessed from the entry script.
2122
+ */
2123
+ function resolvePnpm({ argv } = {}) {
2124
+ const entries = Array.isArray(argv) ? argv : [];
2125
+ for (const entry of entries) if (isExistingPath(entry) && PNPM_SCRIPT.test(entry)) return entry;
2126
+ for (const entry of entries) {
2127
+ const root = dshRuntimeRoot(entry);
2128
+ if (root === void 0) continue;
2129
+ const script = join(root, "..", "pnpm", "bin", "pnpm.mjs");
2130
+ if (existsSync(script)) return script;
2131
+ }
2132
+ }
2133
+ function resolveLauncher({ argv, execPath }) {
2134
+ const pnpm = resolvePnpm({ argv });
2135
+ if (pnpm === void 0) return {
2136
+ command: "pnpm",
2137
+ prefix: []
2138
+ };
2139
+ return {
2140
+ command: execPath,
2141
+ prefix: [pnpm]
2142
+ };
2143
+ }
2144
+ /** Run pnpm host-side. Its output is never returned to the browser. */
2145
+ function spawnRunCommand({ command, args, cwd }, { signal, timeoutMs }) {
2146
+ return new Promise((resolve, reject) => {
2147
+ if (signal?.aborted) {
2148
+ reject(signal.reason instanceof Error ? signal.reason : /* @__PURE__ */ new Error("PackyCode plugin update aborted"));
2149
+ return;
2150
+ }
2151
+ const controller = new AbortController();
2152
+ const onAbort = () => controller.abort(signal.reason);
2153
+ signal?.addEventListener("abort", onAbort, { once: true });
2154
+ const timer = setTimeout(() => controller.abort(/* @__PURE__ */ new Error("PackyCode plugin update timed out")), timeoutMs);
2155
+ let settled = false;
2156
+ const settle = (callback, value) => {
2157
+ if (settled) return;
2158
+ settled = true;
2159
+ clearTimeout(timer);
2160
+ signal?.removeEventListener("abort", onAbort);
2161
+ callback(value);
2162
+ };
2163
+ let child;
2164
+ try {
2165
+ child = spawn(command, args, {
2166
+ stdio: [
2167
+ "ignore",
2168
+ "pipe",
2169
+ "pipe"
2170
+ ],
2171
+ signal: controller.signal,
2172
+ ...cwd === void 0 ? {} : { cwd }
2173
+ });
2174
+ } catch (error) {
2175
+ settle(reject, error);
2176
+ return;
2177
+ }
2178
+ let stdout = "";
2179
+ let stderr = "";
2180
+ child.stdout?.on("data", (chunk) => {
2181
+ stdout = (stdout + chunk).slice(-8192);
2182
+ });
2183
+ child.stderr?.on("data", (chunk) => {
2184
+ stderr = (stderr + chunk).slice(-8192);
2185
+ });
2186
+ child.on("error", (error) => settle(reject, error));
2187
+ child.on("close", (code) => {
2188
+ if (controller.signal.aborted) {
2189
+ const reason = controller.signal.reason;
2190
+ settle(reject, reason instanceof Error ? reason : /* @__PURE__ */ new Error("PackyCode plugin update aborted"));
2191
+ return;
2192
+ }
2193
+ settle(resolve, {
2194
+ code: code ?? 1,
2195
+ stdout,
2196
+ stderr
2197
+ });
2198
+ });
2199
+ });
2200
+ }
2201
+ /**
2202
+ * The running version, the latest registry version, which profile installed
2203
+ * this copy, and registry-spec updates against that profile.
2204
+ */
2205
+ function createPackyCodePluginManager({ fetchImpl = ambientFetch, runCommand = spawnRunCommand, execPath = process.execPath, argv = process.argv, env = process.env, ownPackageJsonUrl = new URL("../package.json", import.meta.url), registryUrl = NPM_REGISTRY_LATEST_URL, now = Date.now, ttlMs = DEFAULT_VERSION_TTL_MS, timeoutMs = DEFAULT_VERSION_TIMEOUT_MS, updateTimeoutMs = DEFAULT_UPDATE_TIMEOUT_MS, settleDelayMs = 250 } = {}) {
2206
+ if (typeof fetchImpl !== "function") throw new Error("PackyCode plugin manager requires fetch");
2207
+ if (typeof runCommand !== "function") throw new Error("PackyCode plugin manager requires runCommand");
2208
+ const dshHome = typeof env.DSH_HOME === "string" && env.DSH_HOME !== "" ? env.DSH_HOME : join(homedir(), ".dsh");
2209
+ const launcher = resolveLauncher({
2210
+ argv,
2211
+ execPath
2212
+ });
2213
+ let cached;
2214
+ let inFlight;
2215
+ let generation = 0;
2216
+ let updating = false;
2217
+ const fetchLatest = async (signal) => {
2218
+ const timeoutSignal = AbortSignal.timeout(timeoutMs);
2219
+ const requestSignal = signal === void 0 ? timeoutSignal : AbortSignal.any([signal, timeoutSignal]);
2220
+ let response;
2221
+ try {
2222
+ response = await fetchImpl(registryUrl, {
2223
+ headers: { Accept: "application/json" },
2224
+ redirect: "error",
2225
+ signal: requestSignal
2226
+ });
2227
+ } catch (error) {
2228
+ if (signal?.aborted) throw error;
2229
+ throw new Error("PackyCode plugin version check failed");
2230
+ }
2231
+ if (!response.ok) throw new Error("PackyCode plugin version check failed");
2232
+ let version;
2233
+ try {
2234
+ version = (await response.json())?.version;
2235
+ } catch {
2236
+ version = void 0;
2237
+ }
2238
+ if (parseSemver(version) === void 0) throw new Error("PackyCode plugin version check failed");
2239
+ return version;
2240
+ };
2241
+ const readOwnVersion = async () => {
2242
+ const version = (await readManifest(fileURLToPath(ownPackageJsonUrl)))?.version;
2243
+ if (parseSemver(version) === void 0) throw new Error("PackyCode plugin version is unavailable");
2244
+ return version;
2245
+ };
2246
+ const load = async (signal) => {
2247
+ signal?.throwIfAborted();
2248
+ const [current, install, latest] = await Promise.all([
2249
+ readOwnVersion(),
2250
+ findInstall({
2251
+ dshHome,
2252
+ ownPackageJsonUrl
2253
+ }),
2254
+ fetchLatest(signal)
2255
+ ]);
2256
+ return {
2257
+ current,
2258
+ latest,
2259
+ updateAvailable: compareSemver(latest, current) === 1,
2260
+ install,
2261
+ fetchedAt: now()
2262
+ };
2263
+ };
2264
+ return Object.freeze({
2265
+ async read({ force = false, signal } = {}) {
2266
+ signal?.throwIfAborted();
2267
+ if (!force && cached !== void 0 && now() - cached.fetchedAt < ttlMs) return clone(cached);
2268
+ const createPending = (requestSignal) => {
2269
+ const observedGeneration = generation;
2270
+ return load(requestSignal).then((value) => {
2271
+ if (generation === observedGeneration) cached = value;
2272
+ return value;
2273
+ });
2274
+ };
2275
+ if (force) return clone(await createPending(signal));
2276
+ const pending = inFlight ?? createPending();
2277
+ if (inFlight === void 0) {
2278
+ inFlight = pending;
2279
+ const clear = () => {
2280
+ if (inFlight === pending) inFlight = void 0;
2281
+ };
2282
+ pending.then(clear, clear);
2283
+ }
2284
+ return clone(await waitForSharedRequest(pending, signal));
2285
+ },
2286
+ async update({ signal } = {}) {
2287
+ signal?.throwIfAborted();
2288
+ if (updating) throw new Error("PackyCode plugin update is already running");
2289
+ updating = true;
2290
+ try {
2291
+ const install = await findInstall({
2292
+ dshHome,
2293
+ ownPackageJsonUrl
2294
+ });
2295
+ if (install.kind === "link") throw new Error("PackyCode plugin is installed from a local checkout");
2296
+ if (install.kind !== "npm" || install.profile === void 0) throw new Error("PackyCode plugin update could not find the owning profile");
2297
+ const latest = await fetchLatest(signal);
2298
+ const profileDir = join(dshHome, "profiles", install.profile);
2299
+ let result;
2300
+ try {
2301
+ result = await runCommand({
2302
+ command: launcher.command,
2303
+ args: [
2304
+ ...launcher.prefix,
2305
+ "add",
2306
+ "--save-exact",
2307
+ `--config.minimumReleaseAge=0`,
2308
+ `${PACKAGE_NAME}@${latest}`
2309
+ ],
2310
+ cwd: profileDir
2311
+ }, {
2312
+ signal,
2313
+ timeoutMs: updateTimeoutMs
2314
+ });
2315
+ } catch (error) {
2316
+ if (signal?.aborted) throw error;
2317
+ throw new Error("PackyCode plugin update failed");
2318
+ }
2319
+ if (result.code !== 0) throw new Error("PackyCode plugin update failed");
2320
+ await confirmInstalled({
2321
+ profileDir,
2322
+ expected: latest,
2323
+ settleDelayMs
2324
+ });
2325
+ generation += 1;
2326
+ cached = void 0;
2327
+ return {
2328
+ version: latest,
2329
+ profile: install.profile
2330
+ };
2331
+ } finally {
2332
+ updating = false;
2333
+ }
2334
+ },
2335
+ invalidate() {
2336
+ generation += 1;
2337
+ cached = void 0;
2338
+ }
2339
+ });
2340
+ }
2341
+ //#endregion
1949
2342
  //#region src/catalog.js
1950
2343
  /**
1951
2344
  * The built-in PackyCode model catalog.
@@ -2354,11 +2747,15 @@ function normalizeHost(value, fallback) {
2354
2747
  * Claude Code CLI manager (optional: without it the CLI endpoints refuse).
2355
2748
  * @returns a handler for `connection.rpc.handle(CHANNEL, handler)`.
2356
2749
  */
2357
- function createPackyCodeRpc({ ring, balance, hosts, preference, claudeCli }) {
2750
+ function createPackyCodeRpc({ ring, balance, hosts, preference, claudeCli, pluginManager }) {
2358
2751
  const requireCli = () => {
2359
2752
  if (claudeCli === void 0) throw new Error("PackyCode Claude Code CLI support is unavailable in this runtime");
2360
2753
  return claudeCli;
2361
2754
  };
2755
+ const requireManager = () => {
2756
+ if (pluginManager === void 0) throw new Error("PackyCode plugin version is unavailable");
2757
+ return pluginManager;
2758
+ };
2362
2759
  const status = async () => ({
2363
2760
  ...await ring.list(),
2364
2761
  hosts: hosts(),
@@ -2368,7 +2765,7 @@ function createPackyCodeRpc({ ring, balance, hosts, preference, claudeCli }) {
2368
2765
  statusHost: DEFAULT_STATUS_HOST
2369
2766
  }
2370
2767
  });
2371
- return async (endpoint, payload) => {
2768
+ return async (endpoint, payload, signal) => {
2372
2769
  try {
2373
2770
  switch (endpoint) {
2374
2771
  case "status": return ok(await status());
@@ -2376,6 +2773,10 @@ function createPackyCodeRpc({ ring, balance, hosts, preference, claudeCli }) {
2376
2773
  await ring.add(payload?.key, payload?.label);
2377
2774
  balance.invalidate();
2378
2775
  return ok(await status());
2776
+ case "keys/rename":
2777
+ await ring.rename(String(payload?.id ?? ""), payload?.label);
2778
+ balance.invalidate();
2779
+ return ok(await status());
2379
2780
  case "keys/remove":
2380
2781
  await ring.remove(String(payload?.id ?? ""));
2381
2782
  balance.invalidate();
@@ -2413,6 +2814,11 @@ function createPackyCodeRpc({ ring, balance, hosts, preference, claudeCli }) {
2413
2814
  cli.install();
2414
2815
  return ok(await cli.status());
2415
2816
  }
2817
+ case "plugin/version": return ok(await requireManager().read({
2818
+ force: payload?.force === true,
2819
+ signal
2820
+ }));
2821
+ case "plugin/update": return ok(await requireManager().update({ signal }));
2416
2822
  default: return failed("Unknown PackyCode endpoint");
2417
2823
  }
2418
2824
  } catch (error) {
@@ -2703,7 +3109,8 @@ function apply(ctx, config) {
2703
3109
  balance,
2704
3110
  hosts,
2705
3111
  preference,
2706
- claudeCli
3112
+ claudeCli,
3113
+ pluginManager: createPackyCodePluginManager()
2707
3114
  });
2708
3115
  /**
2709
3116
  * Connection registers a channel through `owner.webServer`, read on its own
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "dsh-packycode-api",
3
- "version": "1.0.1",
3
+ "version": "1.0.4",
4
4
  "packageManager": "pnpm@10.32.1",
5
5
  "description": "Use a PackyCode (PackyAPI) API key in DeepSeek Harness through a built-in multi-protocol model route with key rotation and balance display",
6
6
  "type": "module",
7
7
  "main": "./lib/index.js",
8
+ "icon": "./icon-subscription.webp",
8
9
  "exports": {
9
10
  ".": "./lib/index.js",
10
11
  "./client": "./lib/client.js",
@@ -12,9 +13,12 @@
12
13
  "./package.json": "./package.json"
13
14
  },
14
15
  "files": [
16
+ "icon-subscription.webp",
15
17
  "lib/*.js",
16
18
  "cordis.patch.yml",
17
19
  "README.md",
20
+ "README.en.md",
21
+ "docs/assets/dsh-packycode-mascot.png",
18
22
  "AGENTS.md",
19
23
  "CHANGELOG.md",
20
24
  "SECURITY.md",