@yanlinglabs/winter-agent-sdk 0.0.41 → 0.0.43

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/index.js CHANGED
@@ -1271,32 +1271,38 @@ function withTestKeychainRedirect(env) {
1271
1271
  return { ...env, [TEST_KEYCHAIN_ENV]: redirect };
1272
1272
  }
1273
1273
  // src/version.ts
1274
- var SDK_VERSION = "0.0.41";
1274
+ var SDK_VERSION = "0.0.43";
1275
1275
  // src/paths/project-key.ts
1276
1276
  var TRANSCRIPT_PROJECT_KEY_MAX_LENGTH = 64;
1277
1277
  var VENDOR_PROJECT_KEY_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
1278
1278
  function isVendorCompliantProjectKey(key) {
1279
1279
  return VENDOR_PROJECT_KEY_PATTERN.test(key);
1280
1280
  }
1281
- function sanitize(absPath) {
1282
- return absPath.replace(/[^a-zA-Z0-9]/g, "-");
1281
+ function isAsciiAlphanumeric(code) {
1282
+ return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
1283
1283
  }
1284
- function rollingHash32(raw) {
1285
- let hash = 0;
1286
- for (let i = 0;i < raw.length; i++) {
1287
- hash = (hash << 5) - hash + raw.charCodeAt(i) | 0;
1284
+ function sanitizeForKey(text) {
1285
+ let out = "";
1286
+ for (let i = 0;i < text.length; i++) {
1287
+ const code = text.charCodeAt(i);
1288
+ out += isAsciiAlphanumeric(code) ? text[i] : "-";
1288
1289
  }
1289
- return hash;
1290
+ return out;
1290
1291
  }
1291
- function overflowSuffix(rawAbsPath) {
1292
- return Math.abs(rollingHash32(rawAbsPath)).toString(36);
1292
+ function hashSuffix(text) {
1293
+ let hash = 0;
1294
+ for (let i = 0;i < text.length; i++) {
1295
+ hash = Math.imul(hash, 31) + text.charCodeAt(i) | 0;
1296
+ }
1297
+ return Math.abs(hash).toString(36);
1293
1298
  }
1294
1299
  function transcriptProjectKey(absPath) {
1295
- const sanitized = sanitize(absPath);
1300
+ const sanitized = sanitizeForKey(absPath);
1296
1301
  if (sanitized.length <= TRANSCRIPT_PROJECT_KEY_MAX_LENGTH)
1297
1302
  return sanitized;
1298
- const suffix = overflowSuffix(absPath);
1299
- return sanitized.slice(0, TRANSCRIPT_PROJECT_KEY_MAX_LENGTH - 1 - suffix.length) + "-" + suffix;
1303
+ const suffix = hashSuffix(absPath);
1304
+ const prefixLength = TRANSCRIPT_PROJECT_KEY_MAX_LENGTH - 1 - suffix.length;
1305
+ return `${sanitized.slice(0, prefixLength)}-${suffix}`;
1300
1306
  }
1301
1307
  // src/paths/keys.ts
1302
1308
  import { realpathSync } from "node:fs";
@@ -2053,52 +2059,52 @@ import { CATALOG_TAG_RENAMES } from "@yanlinglabs/winter-provider-catalog";
2053
2059
 
2054
2060
  // src/plugins/manage.ts
2055
2061
  import { mkdir, open, readFile as readFile2, rename, rmdir, unlink, writeFile } from "node:fs/promises";
2056
- import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, resolve as resolve2 } from "node:path";
2062
+ import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, resolve } from "node:path";
2057
2063
  import { randomBytes } from "node:crypto";
2058
2064
 
2059
2065
  // src/plugins/marketplace-path.ts
2060
- import { resolve, sep } from "node:path";
2061
- var BARE_SOURCE_NAME = /^[A-Za-z0-9][-A-Za-z0-9._]*$/;
2062
- function isBareSourceName(source) {
2063
- return BARE_SOURCE_NAME.test(source) && !source.includes("..");
2064
- }
2065
- function normalizePluginRoot(pluginRoot) {
2066
- if (typeof pluginRoot !== "string" || pluginRoot === "" || pluginRoot.startsWith("/") || pluginRoot.includes("\\") || pluginRoot.includes(":"))
2066
+ import path from "node:path";
2067
+ var MARKETPLACE_DIR = Symbol("marketplace-dir");
2068
+ function usableRoot(pluginRoot) {
2069
+ if (typeof pluginRoot !== "string")
2067
2070
  return;
2068
- const stripped = pluginRoot.replace(/^\.\//, "").replace(/\/+$/, "");
2069
- if (stripped === "" || stripped === ".")
2070
- return ".";
2071
- if (stripped.split("/").some((segment) => segment === "" || segment === "." || segment === ".."))
2071
+ if (pluginRoot === "" || pluginRoot.startsWith("/"))
2072
2072
  return;
2073
- return stripped;
2074
- }
2075
- function withPluginRootPrefix(source, normalizedRoot) {
2076
- if (normalizedRoot === undefined || !isBareSourceName(source))
2077
- return source;
2078
- return normalizedRoot === "." ? `./${source}` : `./${normalizedRoot}/${source}`;
2079
- }
2080
- function schemaSourcePath(source) {
2081
- const normalized = source === "." ? "./" : source;
2082
- return normalized.startsWith("./") ? normalized : undefined;
2083
- }
2084
- function resolveWithinBase(base, relativePath) {
2085
- const normalizedBase = resolve(base);
2086
- const resolved = resolve(base, relativePath);
2087
- if (resolved !== normalizedBase && !resolved.startsWith(normalizedBase + sep)) {
2088
- throw new Error(`Path traversal detected: "${relativePath}" would escape the base directory`);
2073
+ if (pluginRoot.includes("\\") || pluginRoot.includes(":"))
2074
+ return;
2075
+ let rest = pluginRoot.startsWith("./") ? pluginRoot.slice(2) : pluginRoot;
2076
+ rest = rest.replace(/\/+$/, "");
2077
+ if (rest === "" || rest === ".")
2078
+ return MARKETPLACE_DIR;
2079
+ for (const piece of rest.split("/")) {
2080
+ if (piece === "" || piece === "." || piece === "..")
2081
+ return;
2089
2082
  }
2090
- return resolved;
2083
+ return rest;
2084
+ }
2085
+ function isBareName(source) {
2086
+ return /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(source) && !source.includes("..");
2091
2087
  }
2092
2088
  function resolveMarketplacePluginPath(installLocation, pluginRoot, source) {
2093
- if (typeof source !== "string" || source === "")
2094
- return;
2095
- const normalizedRoot = normalizePluginRoot(pluginRoot);
2096
- const prefixed = withPluginRootPrefix(source, normalizedRoot);
2097
- const sourcePath = schemaSourcePath(prefixed);
2098
- if (sourcePath === undefined)
2099
- return;
2100
2089
  try {
2101
- return resolveWithinBase(installLocation, sourcePath);
2090
+ if (typeof source !== "string" || source === "")
2091
+ return;
2092
+ let relativePath = source;
2093
+ if (isBareName(source)) {
2094
+ const root = usableRoot(pluginRoot);
2095
+ if (root !== undefined) {
2096
+ relativePath = root === MARKETPLACE_DIR ? `./${source}` : `./${root}/${source}`;
2097
+ }
2098
+ }
2099
+ if (relativePath === ".")
2100
+ relativePath = "./";
2101
+ if (!relativePath.startsWith("./"))
2102
+ return;
2103
+ const base = path.resolve(installLocation);
2104
+ const candidate = path.resolve(installLocation, relativePath);
2105
+ if (candidate === base || candidate.startsWith(base + path.sep))
2106
+ return candidate;
2107
+ return;
2102
2108
  } catch {
2103
2109
  return;
2104
2110
  }
@@ -2244,7 +2250,7 @@ function classifySource(source) {
2244
2250
  if (/^[a-zA-Z0-9](?:[a-zA-Z0-9._-]*[a-zA-Z0-9])?\/[a-zA-Z0-9](?:[a-zA-Z0-9._-]*[a-zA-Z0-9])?$/.test(source) && !source.includes("..")) {
2245
2251
  return { kind: "github", locator: source };
2246
2252
  }
2247
- return { kind: "directory", locator: isAbsolute2(source) ? source : resolve2(source) };
2253
+ return { kind: "directory", locator: isAbsolute2(source) ? source : resolve(source) };
2248
2254
  }
2249
2255
  function marketplaceInfoOf(name, record) {
2250
2256
  const kind = record.source.source;
@@ -1,3 +1,4 @@
1
1
  export declare const TRANSCRIPT_PROJECT_KEY_MAX_LENGTH = 64;
2
2
  export declare function isVendorCompliantProjectKey(key: string): boolean;
3
+ /** The transcript project key for an absolute path. */
3
4
  export declare function transcriptProjectKey(absPath: string): string;
@@ -1,10 +1 @@
1
- /**
2
- * Resolve one marketplace manifest plugin entry's install path -- the ONE function
3
- * `packages/runtime/src/plugins/installed.ts` (the daemon's own directory-marketplace fallback,
4
- * SV-4) and `packages/sdk/src/plugins/manage.ts` (`resolvePluginSourcePath`, the CLI-facing install
5
- * command) both call, so the two can never resolve the identical manifest entry to two different
6
- * paths. `undefined` for anything invalid or refused -- a bare name with no usable `pluginRoot`, an
7
- * absolute/malformed source, or a path that would escape `installLocation` -- NEVER a throw a
8
- * caller must remember to catch; each caller renders its own typed rejection from `undefined`.
9
- */
10
1
  export declare function resolveMarketplacePluginPath(installLocation: string, pluginRoot: unknown, source: unknown): string | undefined;
@@ -69,12 +69,10 @@ export interface Settings {
69
69
  * `permissions.deny`'s own union exists to prevent; see `unionSandboxFilesystemDenyArrays` below).
70
70
  * CORRECTED in fix round 10, item C: round 9 reasoned `allowWrite` should NOT union (a widening
71
71
  * key should never accumulate) and used the ordinary replace-by-higher-tier merge for it instead
72
- * -- the controller's round-10 ruling reverses this, dump-confirmed against claude's own live
73
- * sandbox-reconciliation code unioning its OWN rule-derived allowWrite set with the native
74
- * sandbox's currently-configured one (`xr=se([...yr,...$r.filter(...)])`, not a replacement).
75
- * Claude itself unions the widening array too; ported here to match. `allowRead` (also new this
76
- * round) is claude's own read carve-out (`getFsReadConfig`'s `allowWithinDeny`) -- carried and
77
- * unioned identically for merge-parity, though `packages/runtime/src/sandbox/profile.ts`'s own
72
+ * -- the controller's round-10 ruling reverses this: Claude Code accumulates allowWrite entries
73
+ * from every source rather than letting one replace another, so Winter unions the widening array
74
+ * too. `allowRead` (also new this round) is the settings key for re-permitting reads inside a
75
+ * denied region -- carried and unioned identically for merge-parity, though `packages/runtime/src/sandbox/profile.ts`'s own
78
76
  * `buildSeatbeltProfile` does not yet enforce it (a disclosed, fails-CLOSED gap: see that file's
79
77
  * own `SandboxFilesystemSettings.allowRead` doc comment).
80
78
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yanlinglabs/winter-agent-sdk",
3
- "version": "0.0.41",
3
+ "version": "0.0.43",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "engines": {
@@ -45,15 +45,15 @@
45
45
  }
46
46
  },
47
47
  "dependencies": {
48
- "@yanlinglabs/winter-provider-catalog": "0.0.41"
48
+ "@yanlinglabs/winter-provider-catalog": "0.0.43"
49
49
  },
50
50
  "optionalDependencies": {
51
- "@yanlinglabs/winter-agent-sdk-darwin-arm64": "0.0.41"
51
+ "@yanlinglabs/winter-agent-sdk-darwin-arm64": "0.0.43"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/node": "^26.4.0",
55
- "@yanlinglabs/winter-conformance": "0.0.41",
56
- "@yanlinglabs/winter-agent-runtime": "0.0.41"
55
+ "@yanlinglabs/winter-agent-runtime": "0.0.43",
56
+ "@yanlinglabs/winter-conformance": "0.0.43"
57
57
  },
58
58
  "scripts": {}
59
59
  }