@yanlinglabs/winter-agent-sdk 0.0.17 → 0.0.22
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-qst9rwbb.js → index-0xks1t4r.js} +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +516 -10
- package/dist/options.d.ts +3 -1
- package/dist/paths/global-config.d.ts +7 -0
- package/dist/paths/home.d.ts +15 -3
- package/dist/paths/host-env.d.ts +11 -0
- package/dist/plugins/manage.d.ts +37 -0
- package/dist/plugins/marketplace-path.d.ts +10 -0
- package/dist/protocol/config.d.ts +25 -0
- package/dist/protocol/frames.d.ts +48 -1
- package/dist/query.d.ts +8 -1
- package/dist/settings/types.d.ts +30 -1
- package/dist/tools/index.js +1 -1
- package/package.json +5 -5
|
@@ -118,7 +118,7 @@ function resolveWinterHome2(env, brand) {
|
|
|
118
118
|
return override;
|
|
119
119
|
const profile = e[envName2(b, "PROFILE")];
|
|
120
120
|
const dirName = profile !== undefined && profile.trim() === "dev" ? `${b.homeDirName}-dev` : b.homeDirName;
|
|
121
|
-
return join(homedir(), dirName);
|
|
121
|
+
return join(homedir(), dirName, "sdk");
|
|
122
122
|
}
|
|
123
123
|
function resolveKeychainServiceForProfile2(brand, env, hostSetKeychainService) {
|
|
124
124
|
if (hostSetKeychainService)
|
package/dist/index.d.ts
CHANGED
|
@@ -27,11 +27,13 @@ export { SDK_VERSION } from "./version.js";
|
|
|
27
27
|
export { MESSAGING_CONTROL_SUBTYPES, MESSAGING_CONTROL_SUBTYPE_LIST, MESSAGING_HOST_REQUEST_SUBTYPES, MESSAGING_RUNTIME_REQUEST_SUBTYPES, resolveFacetTarget } from "./protocol/messaging.js";
|
|
28
28
|
export { isRuntimeAddress, isGlobalAgentMessage, isDeliveryOutcome, isListedRuntimeObjectArray, isPermissionClassLabel, isMessagingDeliverRequest, isMessagingChildRequest, isMessagingSubscribeIdleRequest, isMessagingReadNotificationsRequest, isMessagingNotificationsPage, isMessagingIdleNoticePayload, isNotificationRecord, } from "./protocol/messaging.js";
|
|
29
29
|
export type { MessagingControlSubtype, MessagingDeliverRequest, MessagingChildRequest, MessagingSubscribeIdleRequest, MessagingSenderClassResponse, MessagingReadNotificationsRequest, MessagingNotificationsPage, MessagingIdleNoticePayload, } from "./protocol/messaging.js";
|
|
30
|
-
export type { ProtocolVersion, WinterFrame, SdkMessage as ProtocolSdkMessage, InitFrame, UserFrame, DataFrame, ControlRequestFrame, ControlResponseFrame, UnknownFrame, SDKHookStartedMessage, SDKHookProgressMessage, SDKHookResponseMessage, SDKPermissionDeniedMessage, SDKPermissionDenial, SDKTaskStartedMessage, SDKTaskNotificationMessage, SDKTaskUpdatedMessage, SDKTaskProgressMessage, SDKBackgroundTasksChangedMessage, SDKLocalCommandOutputMessage, BackgroundTaskMessage, SDKCompactBoundaryMessage, SDKSessionStateChangedMessage, WireContentBlock, WireStreamEvent, WireStreamEventDelta, SDKPartialAssistantMessage, SDKAssistantMessageError, SDKAPIRetryMessage, SDKRateLimitEvent, SDKRateLimitInfo, SDKAuthStatusMessage, SDKThinkingTokensMessage, SDKModelRefusalFallbackMessage, SDKModelRefusalNoFallbackMessage, SDKReasoningSummaryMessage, SDKModelSwitchMessage, SDKContinuityWarningMessage, } from "./protocol/frames.js";
|
|
30
|
+
export type { ProtocolVersion, WinterFrame, SdkMessage as ProtocolSdkMessage, InitFrame, UserFrame, DataFrame, ControlRequestFrame, ControlResponseFrame, ControlCancelRequestFrame, WireResultUsage, UnknownFrame, SDKHookStartedMessage, SDKHookProgressMessage, SDKHookResponseMessage, SDKPermissionDeniedMessage, SDKPermissionDenial, SDKTaskStartedMessage, SDKTaskNotificationMessage, SDKTaskUpdatedMessage, SDKTaskProgressMessage, SDKBackgroundTasksChangedMessage, SDKLocalCommandOutputMessage, BackgroundTaskMessage, SDKCompactBoundaryMessage, SDKSessionStateChangedMessage, WireContentBlock, WireStreamEvent, WireStreamEventDelta, SDKPartialAssistantMessage, SDKAssistantMessageError, SDKAPIRetryMessage, SDKRateLimitEvent, SDKRateLimitInfo, SDKAuthStatusMessage, SDKThinkingTokensMessage, SDKModelRefusalFallbackMessage, SDKModelRefusalNoFallbackMessage, SDKReasoningSummaryMessage, SDKModelSwitchMessage, SDKContinuityWarningMessage, } from "./protocol/frames.js";
|
|
31
31
|
export { resolveWinterHome, resolveKeychainServiceForProfile, isUnset } from "./paths/home.js";
|
|
32
32
|
export { transcriptProjectKey, TRANSCRIPT_PROJECT_KEY_MAX_LENGTH, isVendorCompliantProjectKey } from "./paths/project-key.js";
|
|
33
33
|
export { compatibilityKeys } from "./paths/keys.js";
|
|
34
34
|
export type { CompatibilityKeys } from "./paths/keys.js";
|
|
35
|
+
export { globalConfigFileName } from "./paths/global-config.js";
|
|
36
|
+
export { storeHomeEnvName, pluginCacheDirEnvName, providerManagedByHostEnvName, disableCronEnvName } from "./paths/host-env.js";
|
|
35
37
|
export { WinterCompatibilitySessionStore, WinterStoreError, WinterStoreLeaseError, DIALECT_RECORD_ENTRY_TYPE, PROVIDER_STATE_FILE_SUFFIX, } from "./store/session-store.js";
|
|
36
38
|
export type { SessionKey, SessionStoreEntry, SessionSummaryEntry, SessionStore } from "./store/session-store.js";
|
|
37
39
|
export { forkSessionByKey } from "./store/fork-session.js";
|
|
@@ -49,3 +51,7 @@ export type { PermissionUpdate, PermissionUpdateDestination, RuleSource } from "
|
|
|
49
51
|
export type { CanUseTool, PermissionResult, PermissionDecisionClassification, PermissionRequestPayload } from "./permissions/types.js";
|
|
50
52
|
export { HOOK_EVENTS } from "./permissions/types.js";
|
|
51
53
|
export type { HookEvent, HookEventName, HookSource, HookPermissionDecision, BaseHookInput, PreToolUseHookInput, PostToolUseHookInput, PostToolUseFailureHookInput, UserPromptSubmitHookInput, StopHookInput, SessionStartHookInput, SessionEndHookInput, NotificationHookInput, PermissionRequestHookInput, PermissionDeniedHookInput, GenericHookInput, HookInput, AsyncHookJSONOutput, SyncHookJSONOutput, HookJSONOutput, PreToolUseHookSpecificOutput, PostToolUseHookSpecificOutput, PostToolUseFailureHookSpecificOutput, UserPromptSubmitHookSpecificOutput, StopHookSpecificOutput, SessionStartHookSpecificOutput, NotificationHookSpecificOutput, PermissionRequestHookSpecificOutput, PermissionDeniedHookSpecificOutput, GenericHookSpecificOutput, HookSpecificOutput, HookCallback, HookCallbackMatcher, HookInvocationPayload, } from "./permissions/types.js";
|
|
54
|
+
export { CATALOG_TAG_RENAMES } from "@yanlinglabs/winter-provider-catalog";
|
|
55
|
+
export { PluginManagerError, listMarketplaces, addMarketplace, removeMarketplace, updateMarketplace, installPlugin, uninstallPlugin, setPluginEnabled, updatePlugin, listPlugins } from "./plugins/manage.js";
|
|
56
|
+
export type { PluginScope, PluginManagerOptions, MarketplaceInfo, InstalledPlugin, PluginListing } from "./plugins/manage.js";
|
|
57
|
+
export { resolveMarketplacePluginPath } from "./plugins/marketplace-path.js";
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
DIALECT_RECORD_ENTRY_TYPE2,
|
|
15
15
|
PROVIDER_STATE_FILE_SUFFIX2,
|
|
16
16
|
WinterCompatibilitySessionStore2
|
|
17
|
-
} from "./index-
|
|
17
|
+
} from "./index-0xks1t4r.js";
|
|
18
18
|
import {
|
|
19
19
|
deliveryUncertain2,
|
|
20
20
|
refused2,
|
|
@@ -666,6 +666,7 @@ function query(args) {
|
|
|
666
666
|
proc.stdin.write(encodeFrame(frame));
|
|
667
667
|
}
|
|
668
668
|
const controlRequestHandlers = new Map;
|
|
669
|
+
const incomingRequestAborts = new Map;
|
|
669
670
|
async function handleIncomingControlRequest(cf) {
|
|
670
671
|
try {
|
|
671
672
|
const handler = controlRequestHandlers.get(cf.subtype);
|
|
@@ -678,8 +679,12 @@ function query(args) {
|
|
|
678
679
|
});
|
|
679
680
|
return;
|
|
680
681
|
}
|
|
682
|
+
const cancel = new AbortController;
|
|
683
|
+
incomingRequestAborts.set(cf.requestId, cancel);
|
|
681
684
|
try {
|
|
682
|
-
const result = await handler(cf.payload);
|
|
685
|
+
const result = await handler(cf.payload, { signal: cancel.signal });
|
|
686
|
+
if (cancel.signal.aborted)
|
|
687
|
+
return;
|
|
683
688
|
if (result.ok) {
|
|
684
689
|
writeControlResponse({
|
|
685
690
|
type: "control_response",
|
|
@@ -691,19 +696,27 @@ function query(args) {
|
|
|
691
696
|
writeControlResponse({ type: "control_response", requestId: cf.requestId, ok: false, error: result.error });
|
|
692
697
|
}
|
|
693
698
|
} catch (err) {
|
|
699
|
+
if (cancel.signal.aborted)
|
|
700
|
+
return;
|
|
694
701
|
const message = err instanceof Error ? err.message : String(err);
|
|
695
702
|
writeControlResponse({ type: "control_response", requestId: cf.requestId, ok: false, error: { code: "handler_threw", message } });
|
|
703
|
+
} finally {
|
|
704
|
+
incomingRequestAborts.delete(cf.requestId);
|
|
696
705
|
}
|
|
697
706
|
} catch {}
|
|
698
707
|
}
|
|
699
708
|
function makePermissionHandler(canUseTool) {
|
|
700
|
-
return async (payload) => {
|
|
709
|
+
return async (payload, handlerCtx) => {
|
|
701
710
|
const req = payload;
|
|
702
711
|
const controller = new AbortController;
|
|
703
712
|
if (options.abortController?.signal.aborted)
|
|
704
713
|
controller.abort();
|
|
705
714
|
else
|
|
706
715
|
options.abortController?.signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
716
|
+
if (handlerCtx?.signal.aborted === true)
|
|
717
|
+
controller.abort();
|
|
718
|
+
else
|
|
719
|
+
handlerCtx?.signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
707
720
|
let result;
|
|
708
721
|
try {
|
|
709
722
|
result = await canUseTool(req.toolName, req.input, {
|
|
@@ -828,6 +841,13 @@ function query(args) {
|
|
|
828
841
|
}
|
|
829
842
|
continue;
|
|
830
843
|
}
|
|
844
|
+
if (frame.type === "control_cancel_request") {
|
|
845
|
+
const cancelledId = frame.requestId ?? "";
|
|
846
|
+
const cancelled = incomingRequestAborts.get(cancelledId);
|
|
847
|
+
incomingRequestAborts.delete(cancelledId);
|
|
848
|
+
cancelled?.abort();
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
831
851
|
if (frame.type === "control_request") {
|
|
832
852
|
handleIncomingControlRequest(frame);
|
|
833
853
|
continue;
|
|
@@ -1001,7 +1021,7 @@ function query(args) {
|
|
|
1001
1021
|
return gen;
|
|
1002
1022
|
}
|
|
1003
1023
|
// src/version.ts
|
|
1004
|
-
var SDK_VERSION = "0.0.
|
|
1024
|
+
var SDK_VERSION = "0.0.22";
|
|
1005
1025
|
// src/paths/project-key.ts
|
|
1006
1026
|
var TRANSCRIPT_PROJECT_KEY_MAX_LENGTH = 64;
|
|
1007
1027
|
var VENDOR_PROJECT_KEY_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
|
|
@@ -1044,13 +1064,41 @@ function resolveCanonical(raw) {
|
|
|
1044
1064
|
}
|
|
1045
1065
|
}
|
|
1046
1066
|
function gitCommonRoot(resolvedCwd) {
|
|
1067
|
+
let commonDir;
|
|
1068
|
+
try {
|
|
1069
|
+
const raw = runGit(["-C", resolvedCwd, "rev-parse", "--git-common-dir"]);
|
|
1070
|
+
commonDir = platformNormalize(realpathSync(isAbsolute(raw) ? raw : resolvePath(resolvedCwd, raw)));
|
|
1071
|
+
} catch {
|
|
1072
|
+
return null;
|
|
1073
|
+
}
|
|
1074
|
+
const candidate = dirname2(commonDir);
|
|
1075
|
+
if (isWithin(resolvedCwd, candidate))
|
|
1076
|
+
return candidate;
|
|
1077
|
+
if (registeredWorktrees(commonDir).some((worktree) => isWithin(resolvedCwd, worktree)))
|
|
1078
|
+
return candidate;
|
|
1079
|
+
const configured = configuredWorktree(commonDir);
|
|
1080
|
+
if (configured !== null && isWithin(resolvedCwd, configured))
|
|
1081
|
+
return configured;
|
|
1082
|
+
return null;
|
|
1083
|
+
}
|
|
1084
|
+
function runGit(args) {
|
|
1085
|
+
return execFileSync("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
1086
|
+
}
|
|
1087
|
+
function isWithin(path, dir) {
|
|
1088
|
+
return path === dir || path.startsWith(dir.endsWith("/") ? dir : `${dir}/`);
|
|
1089
|
+
}
|
|
1090
|
+
function registeredWorktrees(commonDir) {
|
|
1091
|
+
try {
|
|
1092
|
+
return runGit(["--git-dir", commonDir, "worktree", "list", "--porcelain"]).split(`
|
|
1093
|
+
`).filter((line) => line.startsWith("worktree ")).map((line) => resolveCanonical(line.slice("worktree ".length)));
|
|
1094
|
+
} catch {
|
|
1095
|
+
return [];
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
function configuredWorktree(commonDir) {
|
|
1047
1099
|
try {
|
|
1048
|
-
const raw =
|
|
1049
|
-
|
|
1050
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
1051
|
-
}).trim();
|
|
1052
|
-
const commonDir = isAbsolute(raw) ? raw : resolvePath(resolvedCwd, raw);
|
|
1053
|
-
return platformNormalize(dirname2(realpathSync(commonDir)));
|
|
1100
|
+
const raw = runGit(["--git-dir", commonDir, "config", "--get", "core.worktree"]);
|
|
1101
|
+
return raw.length > 0 ? resolveCanonical(isAbsolute(raw) ? raw : resolvePath(commonDir, raw)) : null;
|
|
1054
1102
|
} catch {
|
|
1055
1103
|
return null;
|
|
1056
1104
|
}
|
|
@@ -1062,6 +1110,23 @@ function compatibilityKeys(cwd) {
|
|
|
1062
1110
|
const memoryKey = commonRoot === null ? cwdKey : transcriptProjectKey(commonRoot);
|
|
1063
1111
|
return { transcriptProjectKey: cwdKey, memoryProjectKey: memoryKey, tempProjectKey: cwdKey };
|
|
1064
1112
|
}
|
|
1113
|
+
// src/paths/global-config.ts
|
|
1114
|
+
function globalConfigFileName(brand) {
|
|
1115
|
+
return `${brand.homeDirName}.json`;
|
|
1116
|
+
}
|
|
1117
|
+
// src/paths/host-env.ts
|
|
1118
|
+
function storeHomeEnvName(brand) {
|
|
1119
|
+
return envName2(brand, "STORE_HOME");
|
|
1120
|
+
}
|
|
1121
|
+
function pluginCacheDirEnvName(brand) {
|
|
1122
|
+
return envName2(brand, "PLUGIN_CACHE_DIR");
|
|
1123
|
+
}
|
|
1124
|
+
function providerManagedByHostEnvName(brand) {
|
|
1125
|
+
return envName2(brand, "PROVIDER_MANAGED_BY_HOST");
|
|
1126
|
+
}
|
|
1127
|
+
function disableCronEnvName(brand) {
|
|
1128
|
+
return envName2(brand, "DISABLE_CRON");
|
|
1129
|
+
}
|
|
1065
1130
|
// src/store/fork-session.ts
|
|
1066
1131
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1067
1132
|
var IDENTITY_FIELDS = ["providerId", "modelKey", "adapterId", "adapterVersion", "catalogVersion", "authRef", "classifierPin"];
|
|
@@ -1394,6 +1459,45 @@ function unionRuleArray(tiersLowestFirst, key) {
|
|
|
1394
1459
|
}
|
|
1395
1460
|
return out.length > 0 ? out : undefined;
|
|
1396
1461
|
}
|
|
1462
|
+
function unionSandboxFilesystemArray(tiersLowestFirst, key) {
|
|
1463
|
+
const seen = new Set;
|
|
1464
|
+
const out = [];
|
|
1465
|
+
for (const tier of tiersLowestFirst) {
|
|
1466
|
+
const sandbox = tier.values["sandbox"];
|
|
1467
|
+
if (!isPlainObject2(sandbox))
|
|
1468
|
+
continue;
|
|
1469
|
+
const filesystem = sandbox["filesystem"];
|
|
1470
|
+
if (!isPlainObject2(filesystem))
|
|
1471
|
+
continue;
|
|
1472
|
+
for (const rule of stringArrayOrUndefined(filesystem[key]) ?? []) {
|
|
1473
|
+
if (seen.has(rule))
|
|
1474
|
+
continue;
|
|
1475
|
+
seen.add(rule);
|
|
1476
|
+
out.push(rule);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
return out.length > 0 ? out : undefined;
|
|
1480
|
+
}
|
|
1481
|
+
function unionSandboxFilesystemDenyArrays(effective, tiersLowestFirst) {
|
|
1482
|
+
const mergedSandbox = effective["sandbox"];
|
|
1483
|
+
const mergedFilesystem = isPlainObject2(mergedSandbox) ? mergedSandbox["filesystem"] : undefined;
|
|
1484
|
+
const filesystem = isPlainObject2(mergedFilesystem) ? { ...mergedFilesystem } : {};
|
|
1485
|
+
let any = isPlainObject2(mergedFilesystem);
|
|
1486
|
+
for (const key of ["allowWrite", "denyWrite", "allowRead", "denyRead"]) {
|
|
1487
|
+
const unioned = unionSandboxFilesystemArray(tiersLowestFirst, key);
|
|
1488
|
+
if (unioned === undefined)
|
|
1489
|
+
delete filesystem[key];
|
|
1490
|
+
else {
|
|
1491
|
+
filesystem[key] = unioned;
|
|
1492
|
+
any = true;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
if (!any)
|
|
1496
|
+
return;
|
|
1497
|
+
const sandbox = isPlainObject2(mergedSandbox) ? { ...mergedSandbox } : {};
|
|
1498
|
+
sandbox["filesystem"] = filesystem;
|
|
1499
|
+
effective["sandbox"] = sandbox;
|
|
1500
|
+
}
|
|
1397
1501
|
function restrictProviderEnables(effective, tiersLowestFirst) {
|
|
1398
1502
|
const disabled = new Set;
|
|
1399
1503
|
for (const tier of tiersLowestFirst) {
|
|
@@ -1492,6 +1596,7 @@ async function resolveSettingsDetailed(opts = {}) {
|
|
|
1492
1596
|
const overlayFilteredTiers = lowestFirst.map((entry) => ({ values: tierContribution(entry.values, entry.source === "project", trustedWorkspace) }));
|
|
1493
1597
|
unionPermissionRuleArrays(effective, overlayFilteredTiers);
|
|
1494
1598
|
restrictProviderEnables(effective, overlayFilteredTiers);
|
|
1599
|
+
unionSandboxFilesystemDenyArrays(effective, overlayFilteredTiers);
|
|
1495
1600
|
if (!trustedWorkspace) {
|
|
1496
1601
|
const projectIndex = lowestFirst.findIndex((e) => e.source === "project");
|
|
1497
1602
|
if (projectIndex !== -1) {
|
|
@@ -1692,9 +1797,394 @@ var HOOK_EVENTS = [
|
|
|
1692
1797
|
"FileChanged",
|
|
1693
1798
|
"DirectoryAdded"
|
|
1694
1799
|
];
|
|
1800
|
+
|
|
1801
|
+
// src/index.ts
|
|
1802
|
+
import { CATALOG_TAG_RENAMES } from "@yanlinglabs/winter-provider-catalog";
|
|
1803
|
+
|
|
1804
|
+
// src/plugins/manage.ts
|
|
1805
|
+
import { mkdir, open, readFile as readFile2, rename, rmdir, unlink, writeFile } from "node:fs/promises";
|
|
1806
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, resolve as resolve2 } from "node:path";
|
|
1807
|
+
import { randomBytes } from "node:crypto";
|
|
1808
|
+
|
|
1809
|
+
// src/plugins/marketplace-path.ts
|
|
1810
|
+
import { resolve, sep } from "node:path";
|
|
1811
|
+
var BARE_SOURCE_NAME = /^[A-Za-z0-9][-A-Za-z0-9._]*$/;
|
|
1812
|
+
function isBareSourceName(source) {
|
|
1813
|
+
return BARE_SOURCE_NAME.test(source) && !source.includes("..");
|
|
1814
|
+
}
|
|
1815
|
+
function normalizePluginRoot(pluginRoot) {
|
|
1816
|
+
if (typeof pluginRoot !== "string" || pluginRoot === "" || pluginRoot.startsWith("/") || pluginRoot.includes("\\") || pluginRoot.includes(":"))
|
|
1817
|
+
return;
|
|
1818
|
+
const stripped = pluginRoot.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
1819
|
+
if (stripped === "" || stripped === ".")
|
|
1820
|
+
return ".";
|
|
1821
|
+
if (stripped.split("/").some((segment) => segment === "" || segment === "." || segment === ".."))
|
|
1822
|
+
return;
|
|
1823
|
+
return stripped;
|
|
1824
|
+
}
|
|
1825
|
+
function withPluginRootPrefix(source, normalizedRoot) {
|
|
1826
|
+
if (normalizedRoot === undefined || !isBareSourceName(source))
|
|
1827
|
+
return source;
|
|
1828
|
+
return normalizedRoot === "." ? `./${source}` : `./${normalizedRoot}/${source}`;
|
|
1829
|
+
}
|
|
1830
|
+
function schemaSourcePath(source) {
|
|
1831
|
+
const normalized = source === "." ? "./" : source;
|
|
1832
|
+
return normalized.startsWith("./") ? normalized : undefined;
|
|
1833
|
+
}
|
|
1834
|
+
function resolveWithinBase(base, relativePath) {
|
|
1835
|
+
const normalizedBase = resolve(base);
|
|
1836
|
+
const resolved = resolve(base, relativePath);
|
|
1837
|
+
if (resolved !== normalizedBase && !resolved.startsWith(normalizedBase + sep)) {
|
|
1838
|
+
throw new Error(`Path traversal detected: "${relativePath}" would escape the base directory`);
|
|
1839
|
+
}
|
|
1840
|
+
return resolved;
|
|
1841
|
+
}
|
|
1842
|
+
function resolveMarketplacePluginPath(installLocation, pluginRoot, source) {
|
|
1843
|
+
if (typeof source !== "string" || source === "")
|
|
1844
|
+
return;
|
|
1845
|
+
const normalizedRoot = normalizePluginRoot(pluginRoot);
|
|
1846
|
+
const prefixed = withPluginRootPrefix(source, normalizedRoot);
|
|
1847
|
+
const sourcePath = schemaSourcePath(prefixed);
|
|
1848
|
+
if (sourcePath === undefined)
|
|
1849
|
+
return;
|
|
1850
|
+
try {
|
|
1851
|
+
return resolveWithinBase(installLocation, sourcePath);
|
|
1852
|
+
} catch {
|
|
1853
|
+
return;
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
// src/plugins/manage.ts
|
|
1858
|
+
class PluginManagerError extends Error {
|
|
1859
|
+
constructor(message) {
|
|
1860
|
+
super(message);
|
|
1861
|
+
this.name = "PluginManagerError";
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
function installedPluginsPath(o) {
|
|
1865
|
+
return join3(o.pluginsRoot, "installed_plugins.json");
|
|
1866
|
+
}
|
|
1867
|
+
function knownMarketplacesPath(o) {
|
|
1868
|
+
return join3(o.pluginsRoot, "known_marketplaces.json");
|
|
1869
|
+
}
|
|
1870
|
+
function isEnoent(err) {
|
|
1871
|
+
return err?.code === "ENOENT";
|
|
1872
|
+
}
|
|
1873
|
+
var RETRYABLE_RENAME_CODES = new Set(["EXDEV", "EPERM", "EEXIST", "EBUSY"]);
|
|
1874
|
+
async function writeJsonAtomicNoLock(path, data) {
|
|
1875
|
+
const body = `${JSON.stringify(data, null, 2)}
|
|
1876
|
+
`;
|
|
1877
|
+
const tmp = `${path}.tmp.${randomBytes(4).toString("hex")}`;
|
|
1878
|
+
let created = false;
|
|
1879
|
+
try {
|
|
1880
|
+
const handle = await open(tmp, "wx", 384);
|
|
1881
|
+
created = true;
|
|
1882
|
+
try {
|
|
1883
|
+
await handle.writeFile(body, "utf8");
|
|
1884
|
+
} finally {
|
|
1885
|
+
await handle.close();
|
|
1886
|
+
}
|
|
1887
|
+
await rename(tmp, path);
|
|
1888
|
+
} catch (err) {
|
|
1889
|
+
const code = err?.code;
|
|
1890
|
+
if (code === undefined || !RETRYABLE_RENAME_CODES.has(code))
|
|
1891
|
+
throw err;
|
|
1892
|
+
await writeFile(path, body, { encoding: "utf8", mode: 384 });
|
|
1893
|
+
if (created) {
|
|
1894
|
+
try {
|
|
1895
|
+
await unlink(tmp);
|
|
1896
|
+
} catch (cleanupErr) {
|
|
1897
|
+
if (!isEnoent(cleanupErr))
|
|
1898
|
+
throw cleanupErr;
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
async function readInstalledPluginsFile(o) {
|
|
1904
|
+
try {
|
|
1905
|
+
const raw = await readFile2(installedPluginsPath(o), "utf8");
|
|
1906
|
+
const parsed = JSON.parse(raw);
|
|
1907
|
+
const plugins = parsed !== null && typeof parsed === "object" && typeof parsed.plugins === "object" && parsed.plugins !== null ? parsed.plugins : {};
|
|
1908
|
+
return { version: 2, plugins };
|
|
1909
|
+
} catch (err) {
|
|
1910
|
+
if (isEnoent(err))
|
|
1911
|
+
return { version: 2, plugins: {} };
|
|
1912
|
+
throw err;
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
var MARKETPLACES_LOCK_ATTEMPTS = 25;
|
|
1916
|
+
var MARKETPLACES_LOCK_RETRY_DELAY_MS = 20;
|
|
1917
|
+
async function withMarketplacesLock(path, fn) {
|
|
1918
|
+
const lockPath = `${path}.lock`;
|
|
1919
|
+
let acquired = false;
|
|
1920
|
+
for (let attempt = 0;attempt < MARKETPLACES_LOCK_ATTEMPTS; attempt++) {
|
|
1921
|
+
try {
|
|
1922
|
+
await mkdir(lockPath);
|
|
1923
|
+
acquired = true;
|
|
1924
|
+
break;
|
|
1925
|
+
} catch (err) {
|
|
1926
|
+
if (err?.code !== "EEXIST")
|
|
1927
|
+
throw err;
|
|
1928
|
+
await new Promise((r) => setTimeout(r, MARKETPLACES_LOCK_RETRY_DELAY_MS));
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
try {
|
|
1932
|
+
return await fn();
|
|
1933
|
+
} finally {
|
|
1934
|
+
if (acquired) {
|
|
1935
|
+
try {
|
|
1936
|
+
await rmdir(lockPath);
|
|
1937
|
+
} catch (err) {
|
|
1938
|
+
if (!isEnoent(err))
|
|
1939
|
+
throw err;
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
async function readKnownMarketplacesFile(o) {
|
|
1945
|
+
try {
|
|
1946
|
+
const raw = await readFile2(knownMarketplacesPath(o), "utf8");
|
|
1947
|
+
const parsed = JSON.parse(raw);
|
|
1948
|
+
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
1949
|
+
} catch (err) {
|
|
1950
|
+
if (isEnoent(err))
|
|
1951
|
+
return {};
|
|
1952
|
+
throw err;
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
async function writeKnownMarketplacesFile(path, file) {
|
|
1956
|
+
await writeJsonAtomicNoLock(path, file);
|
|
1957
|
+
}
|
|
1958
|
+
async function readDirectoryMarketplaceManifest(marketplaceDir) {
|
|
1959
|
+
const manifestPath = join3(marketplaceDir, ".claude-plugin", "marketplace.json");
|
|
1960
|
+
let raw;
|
|
1961
|
+
try {
|
|
1962
|
+
raw = await readFile2(manifestPath, "utf8");
|
|
1963
|
+
} catch (err) {
|
|
1964
|
+
if (isEnoent(err))
|
|
1965
|
+
throw new PluginManagerError(`${manifestPath}: no marketplace manifest there (expected .claude-plugin/marketplace.json under ${marketplaceDir})`);
|
|
1966
|
+
throw err;
|
|
1967
|
+
}
|
|
1968
|
+
let parsed;
|
|
1969
|
+
try {
|
|
1970
|
+
parsed = JSON.parse(raw);
|
|
1971
|
+
} catch (err) {
|
|
1972
|
+
throw new PluginManagerError(`${manifestPath}: malformed JSON (${err.message})`);
|
|
1973
|
+
}
|
|
1974
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
1975
|
+
throw new PluginManagerError(`${manifestPath}: expected a JSON object at the top level`);
|
|
1976
|
+
const obj = parsed;
|
|
1977
|
+
if (typeof obj.name !== "string" || obj.name.length === 0)
|
|
1978
|
+
throw new PluginManagerError(`${manifestPath}: missing required "name"`);
|
|
1979
|
+
if (!Array.isArray(obj.plugins))
|
|
1980
|
+
throw new PluginManagerError(`${manifestPath}: missing required "plugins" array`);
|
|
1981
|
+
return {
|
|
1982
|
+
name: obj.name,
|
|
1983
|
+
plugins: obj.plugins,
|
|
1984
|
+
...obj.version !== undefined ? { version: obj.version } : {},
|
|
1985
|
+
...obj.description !== undefined ? { description: obj.description } : {},
|
|
1986
|
+
...obj.metadata !== undefined ? { metadata: obj.metadata } : {}
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
function classifySource(source) {
|
|
1990
|
+
if (/^https?:\/\//i.test(source))
|
|
1991
|
+
return { kind: "url", locator: source };
|
|
1992
|
+
if (/^git@[^:]+:/i.test(source) || /^git\+/i.test(source) || /\.git$/i.test(source))
|
|
1993
|
+
return { kind: "git", locator: source };
|
|
1994
|
+
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("..")) {
|
|
1995
|
+
return { kind: "github", locator: source };
|
|
1996
|
+
}
|
|
1997
|
+
return { kind: "directory", locator: isAbsolute2(source) ? source : resolve2(source) };
|
|
1998
|
+
}
|
|
1999
|
+
function marketplaceInfoOf(name, record) {
|
|
2000
|
+
const kind = record.source.source;
|
|
2001
|
+
const source = record.source.path ?? record.source.url ?? record.source.repo ?? record.installLocation;
|
|
2002
|
+
return { name, source, kind, path: record.installLocation };
|
|
2003
|
+
}
|
|
2004
|
+
function parseSpec(spec) {
|
|
2005
|
+
const at = spec.lastIndexOf("@");
|
|
2006
|
+
if (at <= 0 || at === spec.length - 1) {
|
|
2007
|
+
throw new PluginManagerError(`plugin spec must be "<name>@<marketplace>" (got ${JSON.stringify(spec)}) -- bare-name marketplace inference is not implemented in this build`);
|
|
2008
|
+
}
|
|
2009
|
+
return { name: spec.slice(0, at), marketplace: spec.slice(at + 1) };
|
|
2010
|
+
}
|
|
2011
|
+
function keyFor(name, marketplace) {
|
|
2012
|
+
return `${name}@${marketplace}`;
|
|
2013
|
+
}
|
|
2014
|
+
async function readEnabledFromSettings(o, scope, key) {
|
|
2015
|
+
const loaded = await loadSettingsFile(o.settingsPathFor(scope));
|
|
2016
|
+
return loaded.values.enabledPlugins?.[key] === true;
|
|
2017
|
+
}
|
|
2018
|
+
function assertSettingsWritable(loaded, path) {
|
|
2019
|
+
if (loaded.present && !loaded.loaded) {
|
|
2020
|
+
throw new PluginManagerError(`${path}: cannot update enabledPlugins -- the file exists but ${loaded.error ?? "could not be read"}; fix it by hand first`);
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
async function setEnabledInSettings(o, scope, key, enabled) {
|
|
2024
|
+
const path = o.settingsPathFor(scope);
|
|
2025
|
+
const loaded = await loadSettingsFile(path);
|
|
2026
|
+
assertSettingsWritable(loaded, path);
|
|
2027
|
+
const settings = { ...loaded.values };
|
|
2028
|
+
const enabledPlugins = { ...settings.enabledPlugins ?? {} };
|
|
2029
|
+
if (enabled === undefined)
|
|
2030
|
+
delete enabledPlugins[key];
|
|
2031
|
+
else
|
|
2032
|
+
enabledPlugins[key] = enabled;
|
|
2033
|
+
settings.enabledPlugins = enabledPlugins;
|
|
2034
|
+
await mkdir(dirname3(path), { recursive: true });
|
|
2035
|
+
await writeJsonAtomicNoLock(path, settings);
|
|
2036
|
+
}
|
|
2037
|
+
async function listMarketplaces(o) {
|
|
2038
|
+
const file = await readKnownMarketplacesFile(o);
|
|
2039
|
+
return Object.entries(file).map(([name, rec]) => marketplaceInfoOf(name, rec));
|
|
2040
|
+
}
|
|
2041
|
+
async function addMarketplace(o, source) {
|
|
2042
|
+
const classification = classifySource(source);
|
|
2043
|
+
if (classification.kind !== "directory") {
|
|
2044
|
+
throw new PluginManagerError(`marketplace source kind "${classification.kind}" is not supported in this build (no network) -- only local directory marketplaces can be added; got ${JSON.stringify(source)}`);
|
|
2045
|
+
}
|
|
2046
|
+
const dir = classification.locator;
|
|
2047
|
+
const manifest = await readDirectoryMarketplaceManifest(dir);
|
|
2048
|
+
await mkdir(o.pluginsRoot, { recursive: true });
|
|
2049
|
+
const path = knownMarketplacesPath(o);
|
|
2050
|
+
const record = {
|
|
2051
|
+
source: { source: "directory", path: dir },
|
|
2052
|
+
installLocation: dir,
|
|
2053
|
+
lastUpdated: new Date().toISOString(),
|
|
2054
|
+
autoUpdate: false
|
|
2055
|
+
};
|
|
2056
|
+
await withMarketplacesLock(path, async () => {
|
|
2057
|
+
const file = await readKnownMarketplacesFile(o);
|
|
2058
|
+
file[manifest.name] = record;
|
|
2059
|
+
await writeKnownMarketplacesFile(path, file);
|
|
2060
|
+
});
|
|
2061
|
+
return marketplaceInfoOf(manifest.name, record);
|
|
2062
|
+
}
|
|
2063
|
+
async function removeMarketplace(o, name) {
|
|
2064
|
+
const path = knownMarketplacesPath(o);
|
|
2065
|
+
await withMarketplacesLock(path, async () => {
|
|
2066
|
+
const file = await readKnownMarketplacesFile(o);
|
|
2067
|
+
if (!(name in file))
|
|
2068
|
+
throw new PluginManagerError(`marketplace "${name}" is not known`);
|
|
2069
|
+
delete file[name];
|
|
2070
|
+
await writeKnownMarketplacesFile(path, file);
|
|
2071
|
+
});
|
|
2072
|
+
}
|
|
2073
|
+
async function updateMarketplace(o, name) {
|
|
2074
|
+
const path = knownMarketplacesPath(o);
|
|
2075
|
+
await withMarketplacesLock(path, async () => {
|
|
2076
|
+
const file = await readKnownMarketplacesFile(o);
|
|
2077
|
+
const names = name !== undefined ? [name] : Object.keys(file);
|
|
2078
|
+
for (const n of names) {
|
|
2079
|
+
const rec = file[n];
|
|
2080
|
+
if (rec === undefined)
|
|
2081
|
+
throw new PluginManagerError(`marketplace "${n}" is not known`);
|
|
2082
|
+
if (rec.source.source !== "directory") {
|
|
2083
|
+
throw new PluginManagerError(`marketplace "${n}" has source kind "${rec.source.source}", which this build cannot refresh (no network)`);
|
|
2084
|
+
}
|
|
2085
|
+
await readDirectoryMarketplaceManifest(rec.installLocation);
|
|
2086
|
+
rec.lastUpdated = new Date().toISOString();
|
|
2087
|
+
}
|
|
2088
|
+
await writeKnownMarketplacesFile(path, file);
|
|
2089
|
+
});
|
|
2090
|
+
}
|
|
2091
|
+
async function resolvePluginSourcePath(o, name, marketplace) {
|
|
2092
|
+
const marketplaces = await readKnownMarketplacesFile(o);
|
|
2093
|
+
const rec = marketplaces[marketplace];
|
|
2094
|
+
if (rec === undefined)
|
|
2095
|
+
throw new PluginManagerError(`marketplace "${marketplace}" is not known -- add it first`);
|
|
2096
|
+
if (rec.source.source !== "directory") {
|
|
2097
|
+
throw new PluginManagerError(`marketplace "${marketplace}" has source kind "${rec.source.source}", which this build cannot install from (no network)`);
|
|
2098
|
+
}
|
|
2099
|
+
const manifest = await readDirectoryMarketplaceManifest(rec.installLocation);
|
|
2100
|
+
const entry = manifest.plugins.find((p) => p.name === name);
|
|
2101
|
+
if (entry === undefined)
|
|
2102
|
+
throw new PluginManagerError(`plugin "${name}" is not listed by marketplace "${marketplace}"`);
|
|
2103
|
+
if (typeof entry.source !== "string") {
|
|
2104
|
+
throw new PluginManagerError(`plugin "${name}@${marketplace}" declares a non-local source, which this build cannot install (no network)`);
|
|
2105
|
+
}
|
|
2106
|
+
const installPath = resolveMarketplacePluginPath(rec.installLocation, manifest.metadata?.pluginRoot, entry.source);
|
|
2107
|
+
if (installPath === undefined) {
|
|
2108
|
+
throw new PluginManagerError(`plugin "${name}@${marketplace}" declares a source (${JSON.stringify(entry.source)}) this build refuses to resolve -- it must be a "./relative/path" under the marketplace root (or a bare name when the marketplace's own metadata.pluginRoot is set), and must not escape the marketplace directory`);
|
|
2109
|
+
}
|
|
2110
|
+
return { installPath, version: entry.version };
|
|
2111
|
+
}
|
|
2112
|
+
async function installPlugin(o, spec, scope) {
|
|
2113
|
+
const { name, marketplace } = parseSpec(spec);
|
|
2114
|
+
const settingsPath = o.settingsPathFor(scope);
|
|
2115
|
+
assertSettingsWritable(await loadSettingsFile(settingsPath), settingsPath);
|
|
2116
|
+
const { installPath, version } = await resolvePluginSourcePath(o, name, marketplace);
|
|
2117
|
+
const key = keyFor(name, marketplace);
|
|
2118
|
+
await mkdir(o.pluginsRoot, { recursive: true });
|
|
2119
|
+
const now = new Date().toISOString();
|
|
2120
|
+
const record = { scope, installPath, installedAt: now, ...version !== undefined ? { version } : {} };
|
|
2121
|
+
const file = await readInstalledPluginsFile(o);
|
|
2122
|
+
const existing = file.plugins[key] ?? [];
|
|
2123
|
+
file.plugins[key] = [...existing.filter((r) => r.scope !== scope), record];
|
|
2124
|
+
await writeJsonAtomicNoLock(installedPluginsPath(o), file);
|
|
2125
|
+
await setEnabledInSettings(o, scope, key, true);
|
|
2126
|
+
return { id: name, installPath, scope, ...version !== undefined ? { version } : {} };
|
|
2127
|
+
}
|
|
2128
|
+
async function uninstallPlugin(o, spec, scope) {
|
|
2129
|
+
const { name, marketplace } = parseSpec(spec);
|
|
2130
|
+
const key = keyFor(name, marketplace);
|
|
2131
|
+
const settingsPath = o.settingsPathFor(scope);
|
|
2132
|
+
assertSettingsWritable(await loadSettingsFile(settingsPath), settingsPath);
|
|
2133
|
+
const file = await readInstalledPluginsFile(o);
|
|
2134
|
+
const existing = file.plugins[key];
|
|
2135
|
+
const remaining = (existing ?? []).filter((r) => r.scope !== scope);
|
|
2136
|
+
if (existing === undefined || remaining.length === existing.length) {
|
|
2137
|
+
throw new PluginManagerError(`plugin "${key}" is not installed at scope "${scope}"`);
|
|
2138
|
+
}
|
|
2139
|
+
if (remaining.length > 0)
|
|
2140
|
+
file.plugins[key] = remaining;
|
|
2141
|
+
else
|
|
2142
|
+
delete file.plugins[key];
|
|
2143
|
+
await writeJsonAtomicNoLock(installedPluginsPath(o), file);
|
|
2144
|
+
await setEnabledInSettings(o, scope, key, undefined);
|
|
2145
|
+
}
|
|
2146
|
+
async function setPluginEnabled(o, spec, scope, enabled) {
|
|
2147
|
+
const { name, marketplace } = parseSpec(spec);
|
|
2148
|
+
const key = keyFor(name, marketplace);
|
|
2149
|
+
const file = await readInstalledPluginsFile(o);
|
|
2150
|
+
if (!(file.plugins[key]?.some((r) => r.scope === scope) ?? false)) {
|
|
2151
|
+
throw new PluginManagerError(`plugin "${key}" is not installed at scope "${scope}"`);
|
|
2152
|
+
}
|
|
2153
|
+
await setEnabledInSettings(o, scope, key, enabled);
|
|
2154
|
+
}
|
|
2155
|
+
async function updatePlugin(o, spec) {
|
|
2156
|
+
const { name, marketplace } = parseSpec(spec);
|
|
2157
|
+
const key = keyFor(name, marketplace);
|
|
2158
|
+
const file = await readInstalledPluginsFile(o);
|
|
2159
|
+
const records = file.plugins[key];
|
|
2160
|
+
if (records === undefined || records.length === 0)
|
|
2161
|
+
throw new PluginManagerError(`plugin "${key}" is not installed`);
|
|
2162
|
+
const { installPath, version } = await resolvePluginSourcePath(o, name, marketplace);
|
|
2163
|
+
const now = new Date().toISOString();
|
|
2164
|
+
const updated = records.map((r) => ({ ...r, installPath, lastUpdated: now, ...version !== undefined ? { version } : {} }));
|
|
2165
|
+
file.plugins[key] = updated;
|
|
2166
|
+
await writeJsonAtomicNoLock(installedPluginsPath(o), file);
|
|
2167
|
+
const last = updated[updated.length - 1];
|
|
2168
|
+
return { id: name, installPath, scope: last.scope, ...version !== undefined ? { version } : {} };
|
|
2169
|
+
}
|
|
2170
|
+
async function listPlugins(o) {
|
|
2171
|
+
const file = await readInstalledPluginsFile(o);
|
|
2172
|
+
const out = [];
|
|
2173
|
+
for (const [key, records] of Object.entries(file.plugins)) {
|
|
2174
|
+
const at = key.lastIndexOf("@");
|
|
2175
|
+
const name = at > 0 ? key.slice(0, at) : key;
|
|
2176
|
+
const marketplace = at > 0 ? key.slice(at + 1) : "";
|
|
2177
|
+
for (const rec of records) {
|
|
2178
|
+
const enabled = await readEnabledFromSettings(o, rec.scope, key);
|
|
2179
|
+
out.push({ id: name, installPath: rec.installPath, scope: rec.scope, enabled, marketplace, ...rec.version !== undefined ? { version: rec.version } : {} });
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
return out;
|
|
2183
|
+
}
|
|
1695
2184
|
export {
|
|
1696
2185
|
AbortError,
|
|
1697
2186
|
BRAND_TOKEN_RE2 as BRAND_TOKEN_RE,
|
|
2187
|
+
CATALOG_TAG_RENAMES,
|
|
1698
2188
|
CLIConnectionError,
|
|
1699
2189
|
DEFAULT_COMPACTION_THRESHOLD,
|
|
1700
2190
|
DEFAULT_CONTEXT_WINDOW_TOKENS,
|
|
@@ -1715,6 +2205,7 @@ export {
|
|
|
1715
2205
|
PROJECT_PERMISSIVE_KEYS,
|
|
1716
2206
|
PROTOCOL_VERSION,
|
|
1717
2207
|
PROVIDER_STATE_FILE_SUFFIX2 as PROVIDER_STATE_FILE_SUFFIX,
|
|
2208
|
+
PluginManagerError,
|
|
1718
2209
|
ProcessError,
|
|
1719
2210
|
ProtocolDecodeError,
|
|
1720
2211
|
ProtocolError,
|
|
@@ -1732,11 +2223,13 @@ export {
|
|
|
1732
2223
|
WinterSDKError,
|
|
1733
2224
|
WinterStoreError2 as WinterStoreError,
|
|
1734
2225
|
WinterStoreLeaseError2 as WinterStoreLeaseError,
|
|
2226
|
+
addMarketplace,
|
|
1735
2227
|
applyWorkspaceTrust,
|
|
1736
2228
|
compatibilityKeys,
|
|
1737
2229
|
decodeFrame,
|
|
1738
2230
|
defaultSpawn,
|
|
1739
2231
|
deleteSession,
|
|
2232
|
+
disableCronEnvName,
|
|
1740
2233
|
encodeFrame,
|
|
1741
2234
|
envName2 as envName,
|
|
1742
2235
|
filterEscalatingDefaultMode,
|
|
@@ -1745,6 +2238,8 @@ export {
|
|
|
1745
2238
|
getSessionInfo,
|
|
1746
2239
|
getSessionMessages,
|
|
1747
2240
|
getSubagentMessages,
|
|
2241
|
+
globalConfigFileName,
|
|
2242
|
+
installPlugin,
|
|
1748
2243
|
isDeliveryOutcome,
|
|
1749
2244
|
isGlobalAgentMessage,
|
|
1750
2245
|
isListedRuntimeObjectArray,
|
|
@@ -1760,25 +2255,36 @@ export {
|
|
|
1760
2255
|
isUnset2 as isUnset,
|
|
1761
2256
|
isVendorCompliantProjectKey,
|
|
1762
2257
|
isWinterMcpServerInstance,
|
|
2258
|
+
listMarketplaces,
|
|
2259
|
+
listPlugins,
|
|
1763
2260
|
listSessions,
|
|
1764
2261
|
listSubagents,
|
|
1765
2262
|
loadSettingsFile,
|
|
1766
2263
|
mcpToolName2 as mcpToolName,
|
|
2264
|
+
pluginCacheDirEnvName,
|
|
2265
|
+
providerManagedByHostEnvName,
|
|
1767
2266
|
providerSettingsFrom,
|
|
1768
2267
|
query,
|
|
2268
|
+
removeMarketplace,
|
|
1769
2269
|
renameSession,
|
|
1770
2270
|
resolveBrand2 as resolveBrand,
|
|
1771
2271
|
resolveFacetTarget,
|
|
1772
2272
|
resolveKeychainServiceForProfile2 as resolveKeychainServiceForProfile,
|
|
2273
|
+
resolveMarketplacePluginPath,
|
|
1773
2274
|
resolveRuntimeExecutable,
|
|
1774
2275
|
resolveSettings,
|
|
1775
2276
|
resolveSettingsDetailed,
|
|
1776
2277
|
resolveWebToolsConfig,
|
|
1777
2278
|
resolveWinterHome2 as resolveWinterHome,
|
|
2279
|
+
setPluginEnabled,
|
|
1778
2280
|
settingsPathFor,
|
|
1779
2281
|
splitFrames,
|
|
2282
|
+
storeHomeEnvName,
|
|
1780
2283
|
tagSession,
|
|
1781
2284
|
transcriptProjectKey,
|
|
2285
|
+
uninstallPlugin,
|
|
2286
|
+
updateMarketplace,
|
|
2287
|
+
updatePlugin,
|
|
1782
2288
|
userAgent2 as userAgent,
|
|
1783
2289
|
validateModelSlots
|
|
1784
2290
|
};
|
package/dist/options.d.ts
CHANGED
|
@@ -275,7 +275,9 @@ export interface Options {
|
|
|
275
275
|
autoClassifier?: AutoClassifierConfig;
|
|
276
276
|
/**
|
|
277
277
|
* DISCLOSED WINTER option (P2 carry, wired in T10): the advisor/reviewer backend's model, same
|
|
278
|
-
* selection path
|
|
278
|
+
* selection path -- a provider-qualified tag (`<providerId>/<model>`) names its own provider, so a
|
|
279
|
+
* reviewer on another provider than the session's is just that tag plus that provider's credential
|
|
280
|
+
* (the `web.fetch.digestModel` shape). Its optional `authRef` (fix wave, Ruling E-1) is the advisor's OWN credential:
|
|
279
281
|
* a target on another provider than the session's never inherits the session's -- it uses the
|
|
280
282
|
* route's ref, else the target provider's own keychain record (`<providerId>:default`), else a
|
|
281
283
|
* typed `no-credential-for-provider` refusal at its first generation.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BrandProfile } from "../brand.js";
|
|
2
|
+
/**
|
|
3
|
+
* `<brand.homeDirName>.json` -- `.winter.json` for Winter's own brand. `brand.homeDirName` is
|
|
4
|
+
* documented (`brand.ts`) as never containing a slash, so this is always a single path segment,
|
|
5
|
+
* safe to `join` onto any directory without further validation.
|
|
6
|
+
*/
|
|
7
|
+
export declare function globalConfigFileName(brand: Pick<BrandProfile, "homeDirName">): string;
|
package/dist/paths/home.d.ts
CHANGED
|
@@ -19,9 +19,21 @@ export type HomeBrand = Pick<BrandProfile, "envPrefix" | "homeDirName">;
|
|
|
19
19
|
* (packages/runtime/src/brand-gate.test.ts, rules 9 and 10) is what keeps it that way.
|
|
20
20
|
*
|
|
21
21
|
* PRECEDENCE (WS-01 §2.2, and the same rule the current daemon has always had): an explicit
|
|
22
|
-
* `<PREFIX>HOME` wins over EVERYTHING, including the dev profile
|
|
23
|
-
* `~/<homeDirName>-dev` — the dev/dist split, so a development
|
|
24
|
-
* transcript store, or a settings file) with the copy a user
|
|
22
|
+
* `<PREFIX>HOME` wins over EVERYTHING, including the dev profile and the WS-21 `sdk` suffix below.
|
|
23
|
+
* `<PREFIX>PROFILE=dev` selects `~/<homeDirName>-dev/sdk` — the dev/dist split, so a development
|
|
24
|
+
* build can never share a home (or a transcript store, or a settings file) with the copy a user
|
|
25
|
+
* actually runs.
|
|
26
|
+
*
|
|
27
|
+
* WS-21 §6.3 item 8: the DEFAULT (no `<PREFIX>HOME`, no explicit `brand`-threaded value) is now
|
|
28
|
+
* `~/<homeDirName>/sdk`, not `~/<homeDirName>` -- Contract A's `sdkHomeOf(home) = join(home, "sdk")`,
|
|
29
|
+
* inlined here rather than imported (the router that exports it is a separate package this SDK
|
|
30
|
+
* never depends on). `brand.homeDirName` cannot contain a slash (`brand.ts`'s own validation), so
|
|
31
|
+
* this is always exactly one extra path segment. This changes ONLY the bare-default case: every
|
|
32
|
+
* router-routed session gets an EXPLICIT `WINTER_HOME` (the Global Constraints list: "only the
|
|
33
|
+
* router sets ... WINTER_HOME"), which still wins outright, and the daemon's own pre-router-link
|
|
34
|
+
* fallback threads its own value too (`session-driver.ts`'s `WINTER_HOME` default, unaffected by
|
|
35
|
+
* this function). A bare `winter` invocation with no env override is the only caller this moves --
|
|
36
|
+
* consistent with the shared runtime home being the thing the SDK itself now means by "home".
|
|
25
37
|
*/
|
|
26
38
|
export declare function resolveWinterHome(env?: Record<string, string | undefined>, brand?: HomeBrand): string;
|
|
27
39
|
/**
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type BrandProfile } from "../brand.js";
|
|
2
|
+
type EnvPrefixBrand = Pick<BrandProfile, "envPrefix">;
|
|
3
|
+
/** `WINTER_STORE_HOME` -- the shared runtime home's durable-paths root (spec §3.7). No claude twin. */
|
|
4
|
+
export declare function storeHomeEnvName(brand: EnvPrefixBrand): string;
|
|
5
|
+
/** `WINTER_PLUGIN_CACHE_DIR` -- the twin of `CLAUDE_CODE_PLUGIN_CACHE_DIR` (F15). */
|
|
6
|
+
export declare function pluginCacheDirEnvName(brand: EnvPrefixBrand): string;
|
|
7
|
+
/** `WINTER_PROVIDER_MANAGED_BY_HOST` -- the twin of `CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST` (F20). */
|
|
8
|
+
export declare function providerManagedByHostEnvName(brand: EnvPrefixBrand): string;
|
|
9
|
+
/** `WINTER_DISABLE_CRON` -- the twin of `CLAUDE_CODE_DISABLE_CRON` (F19a). */
|
|
10
|
+
export declare function disableCronEnvName(brand: EnvPrefixBrand): string;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export declare class PluginManagerError extends Error {
|
|
2
|
+
constructor(message: string);
|
|
3
|
+
}
|
|
4
|
+
export type PluginScope = "user" | "project" | "local";
|
|
5
|
+
export interface PluginManagerOptions {
|
|
6
|
+
/** claude's plugins root (WS-21 §6.3 item 5: `storeHome/plugins`) -- `installed_plugins.json` and `known_marketplaces.json` live directly under it. */
|
|
7
|
+
pluginsRoot: string;
|
|
8
|
+
/** The `enabledPlugins` carrier for one scope -- the settings file at that tier (the shared runtime home's own `settings.json` for `user`; the project dir's `settings.json`/`settings.local.json` for `project`/`local`). */
|
|
9
|
+
settingsPathFor(scope: PluginScope): string;
|
|
10
|
+
}
|
|
11
|
+
export interface MarketplaceInfo {
|
|
12
|
+
name: string;
|
|
13
|
+
/** The human-facing locator: the directory path, the repo `owner/repo`, or the URL -- whichever the source kind carries. */
|
|
14
|
+
source: string;
|
|
15
|
+
kind: "directory" | "git" | "github" | "url";
|
|
16
|
+
/** Where the marketplace's own content lives on disk. For `directory`, this IS the source (read in place, F15). */
|
|
17
|
+
path: string;
|
|
18
|
+
}
|
|
19
|
+
export interface InstalledPlugin {
|
|
20
|
+
id: string;
|
|
21
|
+
version?: string;
|
|
22
|
+
installPath: string;
|
|
23
|
+
scope: PluginScope;
|
|
24
|
+
}
|
|
25
|
+
export interface PluginListing extends InstalledPlugin {
|
|
26
|
+
enabled: boolean;
|
|
27
|
+
marketplace: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function listMarketplaces(o: PluginManagerOptions): Promise<MarketplaceInfo[]>;
|
|
30
|
+
export declare function addMarketplace(o: PluginManagerOptions, source: string): Promise<MarketplaceInfo>;
|
|
31
|
+
export declare function removeMarketplace(o: PluginManagerOptions, name: string): Promise<void>;
|
|
32
|
+
export declare function updateMarketplace(o: PluginManagerOptions, name?: string): Promise<void>;
|
|
33
|
+
export declare function installPlugin(o: PluginManagerOptions, spec: string, scope: PluginScope): Promise<InstalledPlugin>;
|
|
34
|
+
export declare function uninstallPlugin(o: PluginManagerOptions, spec: string, scope: PluginScope): Promise<void>;
|
|
35
|
+
export declare function setPluginEnabled(o: PluginManagerOptions, spec: string, scope: PluginScope, enabled: boolean): Promise<void>;
|
|
36
|
+
export declare function updatePlugin(o: PluginManagerOptions, spec: string): Promise<InstalledPlugin>;
|
|
37
|
+
export declare function listPlugins(o: PluginManagerOptions): Promise<PluginListing[]>;
|
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
export declare function resolveMarketplacePluginPath(installLocation: string, pluginRoot: unknown, source: unknown): string | undefined;
|
|
@@ -88,7 +88,9 @@ export interface SandboxSettingsConfig {
|
|
|
88
88
|
filesystem?: {
|
|
89
89
|
allowWrite?: string[];
|
|
90
90
|
denyWrite?: string[];
|
|
91
|
+
allowRead?: string[];
|
|
91
92
|
denyRead?: string[];
|
|
93
|
+
allowGitConfig?: boolean;
|
|
92
94
|
};
|
|
93
95
|
network?: {
|
|
94
96
|
allowedDomains?: string[];
|
|
@@ -219,6 +221,17 @@ export interface RuntimeConfig {
|
|
|
219
221
|
resumeDropsTurn?: boolean;
|
|
220
222
|
persistSession?: boolean;
|
|
221
223
|
winterHome?: string;
|
|
224
|
+
/**
|
|
225
|
+
* WS-21 §3.7/§6.3 item 11: the shared runtime home's durable-paths root (`storeHomeEnvName`'s
|
|
226
|
+
* value) and the claude-`CLAUDE_CODE_PLUGIN_CACHE_DIR` twin, next to `winterHome` for a host that
|
|
227
|
+
* assembles `--config-json` directly rather than only setting the child's env. The CANONICAL
|
|
228
|
+
* channel is still the child's own env (`production-wiring.ts` reads `config.storeHome ??
|
|
229
|
+
* env[storeHomeEnvName(brand)]`, the same precedence `winterHome` already has over
|
|
230
|
+
* `resolveWinterHome(env, brand)`) -- these two fields exist so a host with no separate env-setting
|
|
231
|
+
* step has an equivalent one.
|
|
232
|
+
*/
|
|
233
|
+
storeHome?: string;
|
|
234
|
+
pluginCacheDir?: string;
|
|
222
235
|
allowedTools?: string[];
|
|
223
236
|
disallowedTools?: string[];
|
|
224
237
|
permissions?: {
|
|
@@ -473,6 +486,18 @@ export interface AutoClassifierConfig {
|
|
|
473
486
|
* advisor's provider, the advisor's provider refuses with a typed `no-credential-for-provider`.
|
|
474
487
|
*/
|
|
475
488
|
export interface AdvisorConfig {
|
|
489
|
+
/**
|
|
490
|
+
* The reviewer's model: a PROVIDER-QUALIFIED TAG (`<providerId>/<model>`, e.g.
|
|
491
|
+
* `codex-oauth/gpt-5.6-sol`) or a slot name, resolved through the SAME selection path as the
|
|
492
|
+
* session model -- exactly as `WebFetchConfig.digestModel` is.
|
|
493
|
+
*
|
|
494
|
+
* THE TAG IS THE PROVIDER IDENTITY; there is deliberately no second field for it. A qualified tag
|
|
495
|
+
* resolves under ITS OWN provider, never under the session's (`Options.provider`), so an advisor on
|
|
496
|
+
* another provider than the session's needs nothing but that tag and, in `authRef`, that provider's
|
|
497
|
+
* own credential. A host that splits the tag down to a bare model id before passing it has thrown
|
|
498
|
+
* the provider away: a bare id is read as a slot name or canonical id and resolved by the session's
|
|
499
|
+
* slot rules, which is a different question.
|
|
500
|
+
*/
|
|
476
501
|
model: string;
|
|
477
502
|
authRef?: CredentialRef;
|
|
478
503
|
}
|
|
@@ -42,11 +42,56 @@ export interface ControlResponseFrame {
|
|
|
42
42
|
message: string;
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Runtime -> host: "I stopped waiting for control_request `requestId`" (lane C, C2 -- claude parity:
|
|
47
|
+
* claude's structuredIO writes `control_cancel_request` when a pending request's signal aborts, e.g.
|
|
48
|
+
* a turn interrupted while a permission prompt is open). The host aborts the callback it is running
|
|
49
|
+
* for that request (`canUseTool`'s `signal`) and owes no response; one sent anyway is dropped.
|
|
50
|
+
*/
|
|
51
|
+
/**
|
|
52
|
+
* `result.usage`: claude's `NonNullableUsage` (every `BetaUsage` field, none null), carrying THIS
|
|
53
|
+
* turn's main-loop generations -- claude builds a fresh QueryEngine per prompt in SDK mode, so its
|
|
54
|
+
* `totalUsage` starts at EMPTY_USAGE each turn. The four token counts are real; the rest is filled the
|
|
55
|
+
* way the pinned binary's own EMPTY_USAGE fills a value it has no data for, and says so:
|
|
56
|
+
* - `cache_creation`: every cache write Winter marks is the default 5-minute ephemeral one (the
|
|
57
|
+
* adapters send `{type: "ephemeral"}` with no ttl), so `ephemeral_5m_input_tokens` equals
|
|
58
|
+
* `cache_creation_input_tokens` and `ephemeral_1h_input_tokens` is 0;
|
|
59
|
+
* - `server_tool_use`: Winter's WebSearch/WebFetch run client-side, never as billed server tools: 0/0;
|
|
60
|
+
* - `output_tokens_details.thinking_tokens`: 0 -- providers' reasoning-token counts are not threaded
|
|
61
|
+
* through Winter's usage yet (they are included in `output_tokens`);
|
|
62
|
+
* - `service_tier: "standard"`, `inference_geo: ""`, `iterations: []`, `speed: "standard"`: the
|
|
63
|
+
* pinned EMPTY_USAGE's own values.
|
|
64
|
+
*/
|
|
65
|
+
export interface WireResultUsage {
|
|
66
|
+
output_tokens_details: {
|
|
67
|
+
thinking_tokens: number;
|
|
68
|
+
};
|
|
69
|
+
input_tokens: number;
|
|
70
|
+
cache_creation_input_tokens: number;
|
|
71
|
+
cache_read_input_tokens: number;
|
|
72
|
+
output_tokens: number;
|
|
73
|
+
server_tool_use: {
|
|
74
|
+
web_search_requests: number;
|
|
75
|
+
web_fetch_requests: number;
|
|
76
|
+
};
|
|
77
|
+
service_tier: "standard" | "priority" | "batch";
|
|
78
|
+
cache_creation: {
|
|
79
|
+
ephemeral_1h_input_tokens: number;
|
|
80
|
+
ephemeral_5m_input_tokens: number;
|
|
81
|
+
};
|
|
82
|
+
inference_geo: string;
|
|
83
|
+
iterations: unknown[];
|
|
84
|
+
speed: "standard" | "fast";
|
|
85
|
+
}
|
|
86
|
+
export interface ControlCancelRequestFrame {
|
|
87
|
+
type: "control_cancel_request";
|
|
88
|
+
requestId: string;
|
|
89
|
+
}
|
|
45
90
|
export interface UnknownFrame {
|
|
46
91
|
type: string;
|
|
47
92
|
[k: string]: unknown;
|
|
48
93
|
}
|
|
49
|
-
export type WinterFrame = InitFrame | UserFrame | DataFrame | ControlRequestFrame | ControlResponseFrame | UnknownFrame;
|
|
94
|
+
export type WinterFrame = InitFrame | UserFrame | DataFrame | ControlRequestFrame | ControlResponseFrame | ControlCancelRequestFrame | UnknownFrame;
|
|
50
95
|
export interface SDKHookStartedMessage {
|
|
51
96
|
type: "system";
|
|
52
97
|
subtype: "hook_started";
|
|
@@ -618,6 +663,8 @@ export type SdkMessage = {
|
|
|
618
663
|
structured_output?: unknown;
|
|
619
664
|
terminal_reason?: "structured_output_retry_exhausted" | "api_error" | string;
|
|
620
665
|
api_error_status?: number | null;
|
|
666
|
+
/** THIS turn's main-loop usage -- claude's `result.usage` (dist-session fixes C1); see `WireResultUsage`. */
|
|
667
|
+
usage?: WireResultUsage;
|
|
621
668
|
permission_denials: SDKPermissionDenial[];
|
|
622
669
|
[k: string]: unknown;
|
|
623
670
|
} | {
|
package/dist/query.d.ts
CHANGED
|
@@ -21,7 +21,14 @@ export type ControlRequestHandlerResult = {
|
|
|
21
21
|
message: string;
|
|
22
22
|
};
|
|
23
23
|
};
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* A handler for one runtime-originated control_request subtype. `ctx.signal` aborts when the runtime
|
|
26
|
+
* CANCELS the request (`control_cancel_request` -- lane C, C2, claude parity): the handler should stop
|
|
27
|
+
* waiting, and whatever it returns afterwards is not sent (the runtime already stopped waiting).
|
|
28
|
+
*/
|
|
29
|
+
export type ControlRequestHandler = (payload: unknown, ctx?: {
|
|
30
|
+
signal: AbortSignal;
|
|
31
|
+
}) => Promise<ControlRequestHandlerResult>;
|
|
25
32
|
export interface QueryInternal {
|
|
26
33
|
registerControlRequestHandler(subtype: string, handler: ControlRequestHandler): void;
|
|
27
34
|
respondPermission(requestId: string, result: PermissionResult): void;
|
package/dist/settings/types.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { BrandProfile } from "../brand.js";
|
|
|
5
5
|
* caller can thread three fields rather than construct one.
|
|
6
6
|
*/
|
|
7
7
|
export type SettingsBrand = Pick<BrandProfile, "envPrefix" | "homeDirName" | "projectDirName">;
|
|
8
|
-
import type { ModelSlotSetting } from "../protocol/config.js";
|
|
8
|
+
import type { ModelSlotSetting, SandboxSettingsConfig } from "../protocol/config.js";
|
|
9
9
|
/** The three settings FILE tiers a session may load. `sdk.d.ts:7917`, verbatim and in pinned order. */
|
|
10
10
|
export type SettingSource = "user" | "project" | "local";
|
|
11
11
|
/** Pinned order (`sdk.d.ts:7917`). Also the value `settingSources: undefined` means (all three). */
|
|
@@ -39,6 +39,35 @@ export interface SettingsPermissionsBlock {
|
|
|
39
39
|
}
|
|
40
40
|
export interface Settings {
|
|
41
41
|
permissions?: SettingsPermissionsBlock;
|
|
42
|
+
/**
|
|
43
|
+
* SV-11 (WS-21 fix round 9, security): the CC-shaped sandbox configuration surface, same shape as
|
|
44
|
+
* `RuntimeConfig.sandbox` (`protocol/config.ts`'s own `SandboxSettingsConfig`, kept structurally
|
|
45
|
+
* identical to `packages/runtime/src/sandbox/profile.ts`'s `SandboxSettings` for the same
|
|
46
|
+
* WS-02 §3 reason that type's own header states). Before this field existed, Winter's runtime read
|
|
47
|
+
* ONLY the host-supplied `RuntimeConfig.sandbox`, ignoring `settings.json`'s own `sandbox` block
|
|
48
|
+
* entirely -- a denied write from a plain, un-escalated `sandbox.filesystem.denyWrite` path went
|
|
49
|
+
* through unsandboxed, silently, because nothing ever threaded that key into the running sandbox at
|
|
50
|
+
* all (not a merge-precedence bug; there was no merge). `production-wiring.ts` now feeds the host's
|
|
51
|
+
* own `RuntimeConfig.sandbox` into this resolution as the `flag` tier (claude's own architecture:
|
|
52
|
+
* its SDK `Options` occupies the identical tier position for this same setting), and reads the
|
|
53
|
+
* result back off `effective.sandbox` into the config it actually hands the engine.
|
|
54
|
+
*
|
|
55
|
+
* ALL FOUR `filesystem` arrays -- `allowWrite`, `denyWrite`, `allowRead`, `denyRead` -- UNION
|
|
56
|
+
* across every tier (deep-merge's ordinary replace-by-higher-tier would let a higher tier
|
|
57
|
+
* silently drop a lower tier's own restriction -- the identical fail-open shape
|
|
58
|
+
* `permissions.deny`'s own union exists to prevent; see `unionSandboxFilesystemDenyArrays` below).
|
|
59
|
+
* CORRECTED in fix round 10, item C: round 9 reasoned `allowWrite` should NOT union (a widening
|
|
60
|
+
* key should never accumulate) and used the ordinary replace-by-higher-tier merge for it instead
|
|
61
|
+
* -- the controller's round-10 ruling reverses this, dump-confirmed against claude's own live
|
|
62
|
+
* sandbox-reconciliation code unioning its OWN rule-derived allowWrite set with the native
|
|
63
|
+
* sandbox's currently-configured one (`xr=se([...yr,...$r.filter(...)])`, not a replacement).
|
|
64
|
+
* Claude itself unions the widening array too; ported here to match. `allowRead` (also new this
|
|
65
|
+
* round) is claude's own read carve-out (`getFsReadConfig`'s `allowWithinDeny`) -- carried and
|
|
66
|
+
* unioned identically for merge-parity, though `packages/runtime/src/sandbox/profile.ts`'s own
|
|
67
|
+
* `buildSeatbeltProfile` does not yet enforce it (a disclosed, fails-CLOSED gap: see that file's
|
|
68
|
+
* own `SandboxFilesystemSettings.allowRead` doc comment).
|
|
69
|
+
*/
|
|
70
|
+
sandbox?: SandboxSettingsConfig;
|
|
42
71
|
hooks?: SettingsHooksConfig;
|
|
43
72
|
env?: Record<string, string>;
|
|
44
73
|
apiKeyHelper?: string;
|
package/dist/tools/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yanlinglabs/winter-agent-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.22",
|
|
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.
|
|
48
|
+
"@yanlinglabs/winter-provider-catalog": "0.0.22"
|
|
49
49
|
},
|
|
50
50
|
"optionalDependencies": {
|
|
51
|
-
"@yanlinglabs/winter-agent-sdk-darwin-arm64": "0.0.
|
|
51
|
+
"@yanlinglabs/winter-agent-sdk-darwin-arm64": "0.0.22"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^26.4.0",
|
|
55
|
-
"winter-
|
|
56
|
-
"
|
|
55
|
+
"@yanlinglabs/winter-conformance": "0.0.22",
|
|
56
|
+
"winter-agent-runtime": "0.0.22"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {}
|
|
59
59
|
}
|