@epoch-agent/core 0.21.0 → 0.23.0

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +258 -16
  2. package/dist/index.js +981 -671
  3. package/package.json +5 -3
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { existsSync, statSync, readdirSync, readFileSync, mkdirSync, writeFileSy
2
2
  import { homedir, platform, tmpdir, hostname } from 'os';
3
3
  import { join, dirname, resolve, relative, isAbsolute, sep, extname, basename } from 'path';
4
4
  import { execFile, execSync, execFileSync, spawn } from 'child_process';
5
- import { createLogger, dbPath, resolveHomeDir, DEFAULT_MAX_TURNS, PROJECT_DIR_NAME, guardSpawn, t, suggestKey, misspelledKeyIssues, openDatabase, migrate, releaseDatabase, parseLenient, issueDetails, splitWords, unknownKeyIssues, checkpointsDir, pluginsDir, projectHooksPath, projectClaudeSettingsPath, projectClaudeLocalSettingsPath, projectSettingsPath, projectLocalSettingsPath, projectPoliciesDir, trustedImportsPath, canProbeGit, budgetStatePath, artifactsDir, UNLIMITED_TURNS, turnLimitOf, configPath, managedSettingsPath, getSecretValues, listStoredSecretNames, maskApiKey, setSectionField, countCodePoints, headCodePoints, attachmentsDir, isInWorkspace, workspacesPath, samePath, lastSessionsPath, appearancePath, automationLogsDir, projectComplianceDir, projectSkillsDir, trustPath, projectCommandsDir, detectBackend, EXEC_PATH_AS_NODE_ENV, isolate, createStreamDecoder, parseShellCommand, writeArtifact, envPath, checkDangerousCommand, checkObfuscation, keepHeadAndTail, normalizeForMatch, isReadOnlyCommand, CODE_EXEC_ROOTS, tailCodePoints } from '@epoch-agent/infra';
5
+ import { createLogger, dbPath, resolveHomeDir, DEFAULT_MAX_TURNS, PROJECT_DIR_NAME, guardSpawn, t, suggestKey, misspelledKeyIssues, openDatabase, migrate, releaseDatabase, parseLenient, issueDetails, splitWords, unknownKeyIssues, checkpointsDir, expandPath, pluginsDir, projectHooksPath, projectClaudeSettingsPath, projectClaudeLocalSettingsPath, projectSettingsPath, projectLocalSettingsPath, projectPoliciesDir, trustedImportsPath, canProbeGit, budgetStatePath, artifactsDir, isInWorkspace, UNLIMITED_TURNS, turnLimitOf, configPath, managedSettingsPath, getSecretValues, listStoredSecretNames, maskApiKey, setSectionField, countCodePoints, headCodePoints, attachmentsDir, workspacesPath, samePath, lastSessionsPath, appearancePath, automationLogsDir, projectComplianceDir, projectSkillsDir, trustPath, projectCommandsDir, detectBackend, EXEC_PATH_AS_NODE_ENV, isolate, createStreamDecoder, parseShellCommand, writeArtifact, envPath, checkDangerousCommand, checkObfuscation, keepHeadAndTail, normalizeForMatch, isReadOnlyCommand, CODE_EXEC_ROOTS, tailCodePoints } from '@epoch-agent/infra';
6
6
  export { IS_WINDOWS, WINDOWS_HIDE_FLAGS, buildBwrapArgs, buildProfile, checkDangerousCommand, checkObfuscation, createLogger, detectBackend, getDefaultShell, getPythonCommand, isInWorkspace, isReadOnlyCommand, isolate, maskApiKey, probeBubblewrap, probeSeatbelt, complianceDir as resolveComplianceDir, policiesDir as resolvePoliciesDir, setLogLevel } from '@epoch-agent/infra';
7
7
  import { PROVIDER_TYPES, OPERATION_TYPES as OPERATION_TYPES$1, COMPLIANCE_CATEGORIES, COMPLIANCE_ACTIONS, SEARCH_PROVIDER_TYPES, CURRENCIES, LANGS, isSettingsPaneId, SETTINGS_PANE_IDS, PERMISSION_LEVELS, SHELL_KINDS, ToolExposure, MAX_OPTIONS, MIN_OPTIONS, MAX_HEADER_CHARS, MAX_QUESTIONS, SCHEDULE_INTERVAL_MINUTES, isPermissionLevel, SCHEDULE_DRAFT_TOOL, isValidAgentRoleName, DEFAULT_AGENT_ROLE, isValidCommandName, RESERVED_COMMAND_NAMES, parseModelRef, isWirePluginArchiveName, systemNote, formatMoney, totalUsageTokens, NOOP_TELEMETRY, METRIC, contentToText, GEN_AI, isPromptOversize, promptTokens, apiKeyEnvVar, PROVIDER_INFOS, isProviderType, API_KEY_ENV_VARS, KEY_ACTIONS, tableOf, perAction, isKeyAction, parseSequence, RESERVED_CHORDS, chordId, sequenceId, ACTION_CONTEXTS, SEQUENCE_CONTEXTS, assertNeverPart, MAX_ATTACH_FILES, MAX_ATTACH_TOTAL_BYTES, MAX_SESSION_REFS, utf8ByteLength, truncateToBudget, attachSessionSurface, isContextNearlyFull, isOperationType, normalizeApproval, imagePartOmittedSummary, MAX_ATTACH_BYTES, SIMULTANEOUS_CONTEXTS, SESSION_REFERENCE_ERROR_CODES, filePartSummary, sessionPartSummary } from '@epoch-agent/protocol';
8
8
  export { API_KEY_ENV_VARS, COMPLIANCE_ACTIONS, COMPLIANCE_CATEGORIES, MAX_ATTACH_BYTES, MAX_ATTACH_FILES, MAX_ATTACH_TOTAL_BYTES, MAX_SESSION_ATTACH_BYTES, MAX_SESSION_REFS, OPERATION_TYPES, PERMISSION_LEVELS, PLAN_OUTCOMES, PROVIDER_INFOS, PROVIDER_TYPES, ToolExposure, apiKeyEnvVar, extractAllMentions, extractMentions, extractSessionMentions, getProviderInfo, isOperationType, isPermissionLevel, isPlanOutcome, isProviderType, normalizeApproval } from '@epoch-agent/protocol';
@@ -487,7 +487,7 @@ function intervalSlots(everyMinutes) {
487
487
  var runCommand2;
488
488
  var init_backend = __esm({
489
489
  "src/schedule/os/backend.ts"() {
490
- runCommand2 = (file, args) => new Promise((resolve16) => {
490
+ runCommand2 = (file, args) => new Promise((resolve17) => {
491
491
  guardSpawn(
492
492
  () => execFile(
493
493
  file,
@@ -495,10 +495,10 @@ var init_backend = __esm({
495
495
  { encoding: "buffer", windowsHide: true },
496
496
  (err, stdout, stderr) => {
497
497
  const code = err && typeof err.code === "number" ? err.code ?? 1 : err ? 1 : 0;
498
- resolve16({ code, stdout: decodeOutput(stdout), stderr: decodeOutput(stderr) });
498
+ resolve17({ code, stdout: decodeOutput(stdout), stderr: decodeOutput(stderr) });
499
499
  }
500
500
  ),
501
- (err) => resolve16({ code: 1, stdout: "", stderr: `${file}: ${err.message}` })
501
+ (err) => resolve17({ code: 1, stdout: "", stderr: `${file}: ${err.message}` })
502
502
  );
503
503
  });
504
504
  }
@@ -2065,7 +2065,7 @@ async function runCode(input) {
2065
2065
  let toolCalls = 0;
2066
2066
  let capped2 = false;
2067
2067
  let ending = null;
2068
- return await new Promise((resolve16) => {
2068
+ return await new Promise((resolve17) => {
2069
2069
  let settled = false;
2070
2070
  const finish = () => {
2071
2071
  if (settled) return;
@@ -2074,7 +2074,7 @@ async function runCode(input) {
2074
2074
  clearInterval(abortWatch);
2075
2075
  void worker.terminate().catch(() => {
2076
2076
  }).then(() => Promise.race([drained, delay(STREAM_DRAIN_MS)])).then(() => {
2077
- resolve16(toOutcome(ending, { printed, toolCalls, capped: capped2, limits}));
2077
+ resolve17(toOutcome(ending, { printed, toolCalls, capped: capped2, limits}));
2078
2078
  });
2079
2079
  };
2080
2080
  const timer = setTimeout(() => {
@@ -2145,14 +2145,14 @@ async function runCode(input) {
2145
2145
  var ABORT_POLL_MS = 100;
2146
2146
  var STREAM_DRAIN_MS = 500;
2147
2147
  function streamEnd(stream) {
2148
- return new Promise((resolve16) => {
2149
- stream.once("end", () => resolve16());
2150
- stream.once("close", () => resolve16());
2148
+ return new Promise((resolve17) => {
2149
+ stream.once("end", () => resolve17());
2150
+ stream.once("close", () => resolve17());
2151
2151
  });
2152
2152
  }
2153
2153
  function delay(ms) {
2154
- return new Promise((resolve16) => {
2155
- setTimeout(resolve16, ms).unref();
2154
+ return new Promise((resolve17) => {
2155
+ setTimeout(resolve17, ms).unref();
2156
2156
  });
2157
2157
  }
2158
2158
  function timeoutMessage(timeoutMs, toolCalls) {
@@ -5287,6 +5287,7 @@ var PROJECT_AGENTS_SUBDIR = join(".epoch", "agents");
5287
5287
  var AGENT_ROLE_DIAG_MODULE = "AgentRole";
5288
5288
  var ROLE_FRONTMATTER_SCHEMA = z.object({
5289
5289
  name: z.string().min(1).optional(),
5290
+ displayName: z.string().min(1).optional(),
5290
5291
  description: z.string().min(1),
5291
5292
  tools: z.array(z.string().min(1)).optional(),
5292
5293
  skills: z.array(z.string().min(1)).optional(),
@@ -5361,6 +5362,7 @@ function parseRoleDefinition(raw, file, source) {
5361
5362
  return {
5362
5363
  role: {
5363
5364
  name,
5365
+ ...fm.displayName ? { displayName: fm.displayName } : {},
5364
5366
  description: fm.description,
5365
5367
  ...prompt ? { prompt } : {},
5366
5368
  ...fm.tools ? { tools: fm.tools } : {},
@@ -5463,8 +5465,17 @@ var BUILTIN_ROLES = [
5463
5465
  source: "builtin"
5464
5466
  }
5465
5467
  ];
5468
+ var BUILTIN_DISPLAY_NAME_KEYS = {
5469
+ [DEFAULT_AGENT_ROLE]: "agent_role.display_general",
5470
+ explore: "agent_role.display_explore",
5471
+ review: "agent_role.display_review"
5472
+ };
5466
5473
  function builtinRoles() {
5467
- return BUILTIN_ROLES.map((r) => cloneRole(r));
5474
+ return BUILTIN_ROLES.map((role) => {
5475
+ const copied = cloneRole(role);
5476
+ const key = BUILTIN_DISPLAY_NAME_KEYS[role.name];
5477
+ return key ? { ...copied, displayName: t(key) } : copied;
5478
+ });
5468
5479
  }
5469
5480
  function cloneRole(role) {
5470
5481
  return {
@@ -5490,6 +5501,7 @@ function mergeRoles(overrides) {
5490
5501
  merged.set(incoming.name, {
5491
5502
  ...existing,
5492
5503
  ...incoming.description ? { description: incoming.description } : {},
5504
+ ...incoming.displayName ? { displayName: incoming.displayName } : {},
5493
5505
  ...incoming.prompt ? { prompt: incoming.prompt } : {},
5494
5506
  ...incoming.maxTurns !== void 0 ? { maxTurns: incoming.maxTurns } : {},
5495
5507
  ...tools ? { tools } : {},
@@ -5569,9 +5581,15 @@ function createRoleFile(input, opts) {
5569
5581
  }
5570
5582
  return { ok: true, path, role: back.role };
5571
5583
  }
5584
+ function displayNameOf(value) {
5585
+ const trimmed = value?.trim();
5586
+ return trimmed ? trimmed : void 0;
5587
+ }
5572
5588
  function frontmatterOf(name, input) {
5589
+ const displayName = displayNameOf(input.displayName);
5573
5590
  return {
5574
5591
  name,
5592
+ ...displayName ? { displayName } : {},
5575
5593
  description: input.description.trim(),
5576
5594
  ...input.tools === void 0 ? {} : { tools: [...input.tools] },
5577
5595
  ...input.skills === void 0 ? {} : { skills: [...input.skills] },
@@ -5588,6 +5606,8 @@ ${body2}${body2 ? "\n" : ""}`;
5588
5606
  }
5589
5607
  function driftOf(role, name, input) {
5590
5608
  if (role.name !== name) return `name: ${role.name} \u2260 ${name}`;
5609
+ if ((role.displayName ?? null) !== (displayNameOf(input.displayName) ?? null))
5610
+ return "displayName";
5591
5611
  if (role.description !== input.description.trim()) return "description";
5592
5612
  if ((role.prompt ?? "") !== (input.prompt ?? "").trim()) return "prompt";
5593
5613
  const tools = input.tools === void 0 ? void 0 : [...input.tools];
@@ -6296,6 +6316,7 @@ var ManifestSchema = z.object({
6296
6316
  epochVersion: z.string().min(1).optional(),
6297
6317
  homepage: z.string().min(1).optional(),
6298
6318
  keywords: z.array(z.string().min(1)).optional(),
6319
+ dependencies: z.array(z.string().min(1)).optional().catch(void 0),
6299
6320
  displayName: z.string().min(1).optional(),
6300
6321
  category: z.string().min(1).optional(),
6301
6322
  icon: z.string().min(1).optional(),
@@ -6338,6 +6359,23 @@ function readPluginManifest(dir) {
6338
6359
  })
6339
6360
  });
6340
6361
  }
6362
+ const rawDependencies = parsed?.dependencies;
6363
+ if (rawDependencies !== void 0 && !Array.isArray(rawDependencies)) {
6364
+ issues.push({
6365
+ path: `${path}.dependencies`,
6366
+ message: t("plugin_manifest.bad_dependencies", { raw: JSON.stringify(rawDependencies) })
6367
+ });
6368
+ }
6369
+ if (Array.isArray(rawDependencies)) {
6370
+ for (const item of rawDependencies) {
6371
+ if (typeof item !== "string" || !isValidPluginName(item)) {
6372
+ issues.push({
6373
+ path: `${path}.dependencies`,
6374
+ message: t("plugin_manifest.bad_dependency_name", { name: String(item) })
6375
+ });
6376
+ }
6377
+ }
6378
+ }
6341
6379
  const checked = parseLenient(ManifestSchema.optional(), parsed, void 0);
6342
6380
  if (!checked.value) {
6343
6381
  issues.push(
@@ -6617,7 +6655,7 @@ var PluginStore = class {
6617
6655
  }
6618
6656
  };
6619
6657
  function sleep(ms) {
6620
- return new Promise((resolve16) => setTimeout(resolve16, ms));
6658
+ return new Promise((resolve17) => setTimeout(resolve17, ms));
6621
6659
  }
6622
6660
  // src/plugin/loader.ts
6623
6661
  var MODULE = "Plugin";
@@ -6639,6 +6677,7 @@ function loadPlugins(opts) {
6639
6677
  const state = readPluginRecords(statePath);
6640
6678
  const out = noPlugins();
6641
6679
  out.issues.push(...state.issues);
6680
+ const declared = /* @__PURE__ */ new Map();
6642
6681
  const epochVersion = opts.epochVersion ?? currentEpochVersion();
6643
6682
  for (const record of state.records) {
6644
6683
  const outcome = admit(record, epochVersion);
@@ -6657,6 +6696,21 @@ function loadPlugins(opts) {
6657
6696
  continue;
6658
6697
  }
6659
6698
  collect(record, out);
6699
+ if (outcome.manifest?.dependencies?.length) {
6700
+ declared.set(record.name, outcome.manifest.dependencies);
6701
+ }
6702
+ }
6703
+ const present = new Set(out.loaded.map((plugin) => plugin.name));
6704
+ for (const [name, dependencies] of declared) {
6705
+ if (!present.has(name)) continue;
6706
+ for (const dependency of dependencies) {
6707
+ if (present.has(dependency)) continue;
6708
+ diags?.warn(
6709
+ MODULE,
6710
+ t("plugin_load.dependency_missing", { name, dependency }),
6711
+ "plugin-dependency-missing"
6712
+ );
6713
+ }
6660
6714
  }
6661
6715
  for (const detail of issueDetails(out.issues)) {
6662
6716
  diags?.warn(MODULE, detail, "plugin-invalid");
@@ -6683,7 +6737,7 @@ function admit(record, epochVersion) {
6683
6737
  if (!version.ok) {
6684
6738
  return { reason: version.reason, code: "plugin-version-mismatch" };
6685
6739
  }
6686
- return {};
6740
+ return { manifest: manifest.manifest };
6687
6741
  }
6688
6742
  function collect(record, out) {
6689
6743
  const { name, path: dir } = record;
@@ -7029,708 +7083,887 @@ function firstLine3(err) {
7029
7083
  const message = err instanceof Error ? err.message : String(err);
7030
7084
  return message.split("\n")[0] ?? message;
7031
7085
  }
7032
- var NPM_SPEC = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*(?:@[a-zA-Z0-9.^~*+-]+)?$/;
7033
- var REMOTE_URL = /^https?:\/\/[^\s'"\\^|<>]+$/;
7034
- function resolveSource(raw) {
7035
- const input = raw.trim();
7036
- if (!input) return void 0;
7037
- if (input.startsWith("github:")) {
7038
- const [slug = "", fragment] = input.slice("github:".length).split("#", 2);
7039
- if (!/^[\w.-]+\/[\w.-]+$/.test(slug)) return void 0;
7040
- const sub = subdirOf(fragment);
7041
- if (!sub.ok) return void 0;
7042
- return {
7043
- type: "git",
7044
- raw: input,
7045
- target: `https://github.com/${slug}.git`,
7046
- ...sub.subdir ? { subdir: sub.subdir } : {}
7047
- };
7048
- }
7049
- if (input.startsWith("git+")) {
7050
- const [url = "", fragment] = input.slice("git+".length).split("#", 2);
7051
- if (!REMOTE_URL.test(url)) return void 0;
7052
- const sub = subdirOf(fragment);
7053
- if (!sub.ok) return void 0;
7054
- return { type: "git", raw: input, target: url, ...sub.subdir ? { subdir: sub.subdir } : {} };
7055
- }
7056
- if (input.startsWith("npm:")) {
7057
- const pkg = input.slice("npm:".length);
7058
- if (!NPM_SPEC.test(pkg)) return void 0;
7059
- return { type: "npm", raw: input, target: pkg };
7060
- }
7061
- if (input.startsWith("host:")) {
7062
- const opaque = input.slice("host:".length).trim();
7063
- if (!opaque) return void 0;
7064
- return { type: "host", raw: input, target: opaque };
7065
- }
7066
- if (input.startsWith("https://") || input.startsWith("http://")) {
7067
- const [url = "", fragment] = input.split("#", 2);
7068
- if (!REMOTE_URL.test(url)) return void 0;
7069
- const sha = fragment?.startsWith("sha256=") ? fragment.slice("sha256=".length) : void 0;
7070
- if (isArchiveUrl(url)) {
7071
- if (sha !== void 0 && !/^[a-f0-9]{64}$/i.test(sha)) return void 0;
7072
- return {
7073
- type: "archive",
7074
- raw: input,
7075
- target: url,
7076
- ...sha ? { sha256: sha.toLowerCase() } : {}
7077
- };
7078
- }
7079
- return { type: "git", raw: input, target: url };
7080
- }
7081
- const abs = isAbsolute(input) ? input : resolve(input);
7082
- if (isArchiveUrl(abs)) {
7083
- return { type: "archive", raw: input, target: abs, localFile: true };
7084
- }
7085
- return { type: "local", raw: input, target: abs };
7086
- }
7087
- function isArchiveUrl(url) {
7088
- return isWirePluginArchiveName(url);
7086
+ var MARKETPLACE_FILE = "epoch-marketplace.json";
7087
+ var NAME_PATTERN = PLUGIN_NAME_PATTERN;
7088
+ var EntryCategoryInput = z.union([z.string().min(1), z.array(z.string().min(1))]).optional().catch(void 0);
7089
+ var EntryShape = z.object({
7090
+ name: z.string().regex(NAME_PATTERN),
7091
+ description: z.string().min(1).optional(),
7092
+ source: z.string().min(1),
7093
+ category: EntryCategoryInput,
7094
+ keywords: z.array(z.string().min(1)).optional(),
7095
+ author: PluginAuthorSchema.optional(),
7096
+ downloads: z.number().int().nonnegative().optional(),
7097
+ likes: z.number().int().nonnegative().optional(),
7098
+ displayName: z.string().min(1).optional(),
7099
+ icon: z.string().min(1).optional(),
7100
+ version: z.string().min(1).optional(),
7101
+ changelog: z.string().min(1).optional(),
7102
+ size: z.number().int().positive().optional(),
7103
+ homepage: z.string().min(1).optional(),
7104
+ visibility: z.enum(PLUGIN_VISIBILITY).optional().catch(void 0),
7105
+ status: z.enum(["listed", "reviewing", "rejected", "offline"]).optional().catch(void 0)
7106
+ });
7107
+ var EntrySchema = EntryShape.transform(({ category, ...rest }) => {
7108
+ const picked = category === void 0 ? [] : typeof category === "string" ? [category] : category;
7109
+ return { ...rest, ...picked.length === 0 ? {} : { category: [...picked] } };
7110
+ });
7111
+ var MARKETPLACE_DISPLAY_FIELDS = [
7112
+ "status",
7113
+ "downloads",
7114
+ "likes",
7115
+ "author",
7116
+ "category",
7117
+ "visibility",
7118
+ "icon",
7119
+ "homepage",
7120
+ "size"
7121
+ ];
7122
+ var DisplaySchema = z.object({
7123
+ fields: z.array(z.string().min(1)).optional()
7124
+ });
7125
+ var CategoryNodeSchema = z.lazy(
7126
+ () => z.object({
7127
+ id: z.string().min(1),
7128
+ label: z.string().min(1).optional(),
7129
+ children: z.array(CategoryNodeSchema).optional()
7130
+ })
7131
+ );
7132
+ var CategoriesSchema = z.array(CategoryNodeSchema).refine(hasUniqueCategoryIds, { message: "category ids must be unique across the whole tree" }).optional().catch(void 0);
7133
+ function hasUniqueCategoryIds(nodes) {
7134
+ const seen = /* @__PURE__ */ new Set();
7135
+ const walk3 = (list) => list.every((node) => {
7136
+ if (seen.has(node.id)) return false;
7137
+ seen.add(node.id);
7138
+ return node.children === void 0 || walk3(node.children);
7139
+ });
7140
+ return walk3(nodes);
7089
7141
  }
7090
- function subdirOf(fragment) {
7091
- if (fragment === void 0 || fragment === "") return { ok: true };
7092
- if (!fragment.startsWith("subdir=")) return { ok: false };
7093
- const rel = fragment.slice("subdir=".length).replace(/\/+$/, "");
7094
- if (!/^[\w.-]+(?:\/[\w.-]+)*$/.test(rel)) return { ok: false };
7095
- if (rel.split("/").includes("..")) return { ok: false };
7096
- return { ok: true, subdir: rel };
7142
+ var CatalogSchema = z.object({
7143
+ name: z.string().regex(NAME_PATTERN),
7144
+ description: z.string().min(1).optional(),
7145
+ owner: z.object({ name: z.string().min(1), url: z.string().min(1).optional() }).optional(),
7146
+ display: DisplaySchema.optional(),
7147
+ categories: CategoriesSchema,
7148
+ plugins: z.array(EntrySchema)
7149
+ });
7150
+ var RecordSchema2 = z.object({
7151
+ name: z.string().regex(NAME_PATTERN),
7152
+ source: z.string().min(1),
7153
+ addedAt: z.number(),
7154
+ catalog: CatalogSchema
7155
+ });
7156
+ var StateSchema2 = z.object({
7157
+ version: z.literal(1).optional(),
7158
+ marketplaces: z.array(RecordSchema2)
7159
+ });
7160
+ function projectEntryForDisplay(entry, display) {
7161
+ const allowed = display?.fields;
7162
+ if (allowed === void 0) return entry;
7163
+ const out = { ...entry };
7164
+ for (const field of MARKETPLACE_DISPLAY_FIELDS) {
7165
+ if (!displayAllows(display, field)) delete out[field];
7166
+ }
7167
+ return out;
7097
7168
  }
7098
- function pluginRoot(source, staging) {
7099
- return source.subdir ? join(staging, source.subdir) : staging;
7169
+ function displayAllows(display, field) {
7170
+ const allowed = display?.fields;
7171
+ if (allowed === void 0) return true;
7172
+ return allowed.includes(field);
7100
7173
  }
7101
- // src/plugin/install.ts
7102
- async function installPlugin(source, opts) {
7103
- const resolved = resolveSource(source);
7104
- if (!resolved) {
7174
+ function readMarketplaces(statePath) {
7175
+ if (!existsSync(statePath)) return { records: [], issues: [] };
7176
+ let raw;
7177
+ try {
7178
+ raw = JSON.parse(readFileSync(statePath, "utf-8"));
7179
+ } catch (err) {
7105
7180
  return {
7106
- ok: false,
7107
- reason: t("plugin_install.bad_source", { source })
7181
+ records: [],
7182
+ issues: [
7183
+ { path: statePath, message: t("plugin_marketplace.bad_json", { reason: first(err) }) }
7184
+ ]
7108
7185
  };
7109
7186
  }
7110
- mkdirSync(opts.pluginsDir, { recursive: true });
7111
- const staging = join(opts.pluginsDir, `.staging-${randomUUID().slice(0, 8)}`);
7187
+ const checked = parseLenient(StateSchema2.optional(), raw, void 0);
7188
+ if (!checked.value) {
7189
+ return {
7190
+ records: [],
7191
+ issues: checked.issues.map((i) => ({
7192
+ path: statePath,
7193
+ message: t("plugin_marketplace.issue", { path: i.path, message: i.message })
7194
+ }))
7195
+ };
7196
+ }
7197
+ return { records: checked.value.marketplaces, issues: [] };
7198
+ }
7199
+ async function mutate(statePath, fn) {
7200
+ const release = await acquireStateLock(statePath);
7112
7201
  try {
7113
- return await runInstall(resolved, staging, opts);
7202
+ const next = fn(readMarketplaces(statePath).records);
7203
+ if (!next) return false;
7204
+ writeStateFile(statePath, JSON.stringify({ version: 1, marketplaces: next }, null, 2));
7205
+ return true;
7114
7206
  } finally {
7115
- rmSync(staging, { recursive: true, force: true });
7207
+ release();
7116
7208
  }
7117
7209
  }
7118
- async function runInstall(resolved, staging, opts) {
7119
- const fetched = await fetchInto(resolved, staging, opts);
7120
- if (!fetched.ok) return { ok: false, reason: fetched.reason };
7121
- const readDir = resolved.type === "local" ? resolved.target : pluginRoot(resolved, staging);
7122
- const read = readPluginManifest(readDir);
7123
- const manifest = read.manifest;
7124
- if (!manifest) {
7125
- const detail = read.issues.map((i) => t("plugin_install.issue_line", { path: i.path, message: i.message })).join("\n");
7126
- return { ok: false, reason: t("plugin_install.manifest_unusable", { detail: `
7127
- ${detail}` }) };
7128
- }
7129
- const taken = new PluginStore(opts.statePath).read().records.find((r) => r.name === manifest.name);
7130
- const conflict = taken && taken.name !== opts.replace ? taken : void 0;
7131
- const current = opts.epochVersion ?? currentEpochVersion();
7132
- const version = checkEpochVersion(manifest.epochVersion, current);
7133
- const versionUnmet = !version.ok && manifest.epochVersion ? { required: manifest.epochVersion, current, reason: version.reason } : void 0;
7134
- const preview3 = {
7135
- manifest,
7136
- source: resolved,
7137
- inventory: scanPluginDir(readDir, manifest.name),
7138
- ...conflict ? { conflict } : {},
7139
- ...versionUnmet ? { versionUnmet } : {}
7210
+ async function addMarketplace(source, opts) {
7211
+ const fetched = await fetchCatalog(source);
7212
+ if (!fetched.ok) return fetched;
7213
+ const record = {
7214
+ name: fetched.catalog.name,
7215
+ source: source.trim(),
7216
+ addedAt: Date.now(),
7217
+ catalog: fetched.catalog
7140
7218
  };
7141
- if (opts.replace !== void 0 && manifest.name !== opts.replace) {
7219
+ const written = await mutate(
7220
+ opts.statePath,
7221
+ (records) => records.some((r) => r.name === record.name) ? void 0 : [...records, record].sort((a, b) => a.name.localeCompare(b.name))
7222
+ );
7223
+ return written ? { ok: true, record } : {
7224
+ ok: false,
7225
+ reason: t("plugin_marketplace.already_added", { name: record.name })
7226
+ };
7227
+ }
7228
+ async function removeMarketplace(name, opts) {
7229
+ const written = await mutate(
7230
+ opts.statePath,
7231
+ (records) => records.some((r) => r.name === name) ? records.filter((r) => r.name !== name) : void 0
7232
+ );
7233
+ return written ? { ok: true } : { ok: false, reason: t("plugin_marketplace.not_added", { name }) };
7234
+ }
7235
+ async function updateMarketplace(name, opts) {
7236
+ const existing = readMarketplaces(opts.statePath).records.find((r) => r.name === name);
7237
+ if (!existing) return { ok: false, reason: t("plugin_marketplace.not_added", { name }) };
7238
+ const fetched = await fetchCatalog(existing.source);
7239
+ if (!fetched.ok) return fetched;
7240
+ if (fetched.catalog.name !== name) {
7142
7241
  return {
7143
7242
  ok: false,
7144
- preview: preview3,
7145
- reason: t("plugin_install.renamed", { found: manifest.name, expected: opts.replace })
7243
+ reason: t("plugin_marketplace.renamed", {
7244
+ source: existing.source,
7245
+ name: fetched.catalog.name
7246
+ })
7146
7247
  };
7147
7248
  }
7148
- if (conflict) {
7249
+ const record = { ...existing, catalog: fetched.catalog };
7250
+ await mutate(opts.statePath, (records) => records.map((r) => r.name === name ? record : r));
7251
+ return { ok: true, record };
7252
+ }
7253
+ function searchMarketplaces(keyword, statePath) {
7254
+ const needle = keyword.trim().toLowerCase();
7255
+ const hits = [];
7256
+ for (const record of readMarketplaces(statePath).records) {
7257
+ for (const entry of record.catalog.plugins) {
7258
+ const haystack = [
7259
+ entry.name,
7260
+ entry.description ?? "",
7261
+ ...entry.category ?? [],
7262
+ entry.author?.name ?? "",
7263
+ ...entry.keywords ?? []
7264
+ ].join(" ").toLowerCase();
7265
+ if (needle && !haystack.includes(needle)) continue;
7266
+ hits.push({
7267
+ marketplace: record.name,
7268
+ entry: projectEntryForDisplay(entry, record.catalog.display),
7269
+ ref: `${record.name}/${entry.name}`
7270
+ });
7271
+ }
7272
+ }
7273
+ return hits;
7274
+ }
7275
+ function resolveMarketplaceRef(input, statePath) {
7276
+ const parts = input.trim().split("/");
7277
+ if (parts.length !== 2) return void 0;
7278
+ const [market = "", plugin = ""] = parts;
7279
+ if (!NAME_PATTERN.test(market) || !NAME_PATTERN.test(plugin)) return void 0;
7280
+ const record = readMarketplaces(statePath).records.find((r) => r.name === market);
7281
+ if (!record) {
7149
7282
  return {
7150
7283
  ok: false,
7151
- preview: preview3,
7152
- reason: t("plugin_install.already_installed", {
7153
- name: conflict.name,
7154
- version: conflict.version,
7155
- source: conflict.source
7156
- })
7284
+ reason: t("plugin_marketplace.market_not_added", { market, input })
7157
7285
  };
7158
7286
  }
7159
- if (opts.confirm && !await opts.confirm(preview3)) {
7160
- return { ok: false, reason: t("plugin.install_cancelled"), cancelled: true, preview: preview3 };
7161
- }
7162
- const record = await commit(resolved, staging, preview3, opts);
7163
- return record ? { ok: true, record, preview: preview3 } : {
7164
- ok: false,
7165
- preview: preview3,
7166
- reason: t("plugin_install.raced", { name: manifest.name })
7167
- };
7168
- }
7169
- async function commit(source, staging, preview3, opts) {
7170
- const name = preview3.manifest.name;
7171
- const dest = join(opts.pluginsDir, name);
7172
- removeInstallDest(dest);
7173
- const linked = source.type === "local";
7174
- if (linked) {
7175
- try {
7176
- symlinkSync(source.target, dest, "junction");
7177
- } catch {
7178
- opts.onProgress?.(t("plugin_install.symlink_failed"));
7179
- cpSync(source.target, dest, { recursive: true });
7180
- return write(false);
7181
- }
7182
- } else {
7183
- renameSync(pluginRoot(source, staging), dest);
7287
+ const entry = record.catalog.plugins.find((p) => p.name === plugin);
7288
+ if (!entry) {
7289
+ return {
7290
+ ok: false,
7291
+ reason: t("plugin_marketplace.plugin_not_in_market", { market, plugin })
7292
+ };
7184
7293
  }
7185
- return write(linked);
7186
- function write(isLink) {
7187
- const record = {
7188
- name,
7189
- version: preview3.manifest.version,
7190
- source: source.raw,
7191
- sourceType: source.type,
7192
- path: dest,
7193
- linked: isLink,
7194
- enabled: true,
7195
- installedAt: Date.now(),
7196
- ...opts.marketplace ? { marketplace: opts.marketplace } : {},
7197
- ...source.sha256 ? { checksum: `sha256:${source.sha256}` } : {}
7294
+ const source = resolveEntrySource(entry.source, record.source);
7295
+ if (!source) {
7296
+ return {
7297
+ ok: false,
7298
+ reason: t("plugin_marketplace.entry_unresolvable", {
7299
+ market,
7300
+ plugin,
7301
+ source: entry.source
7302
+ })
7198
7303
  };
7199
- return new PluginStore(opts.statePath).mutate((records) => {
7200
- const taken = records.some((r) => r.name === name);
7201
- if (taken && opts.replace !== name) return void 0;
7202
- return {
7203
- records: taken ? records.map((r) => r.name === name ? record : r) : [...records, record],
7204
- result: record
7205
- };
7206
- });
7207
7304
  }
7305
+ return { ok: true, ref: { marketplace: market, source } };
7208
7306
  }
7209
- function removeInstallDest(dest) {
7210
- let link = false;
7211
- try {
7212
- link = lstatSync(dest).isSymbolicLink();
7213
- } catch {
7214
- }
7215
- if (!link) {
7216
- rmSync(dest, { recursive: true, force: true });
7217
- return;
7307
+ function resolveEntrySource(entry, marketSource) {
7308
+ const spec = entry.trim();
7309
+ if (!spec.startsWith(".") && !spec.startsWith("/")) return spec;
7310
+ const rel = spec.replace(/^\.\//, "").replace(/^\//, "");
7311
+ if (rel === "" || rel.split("/").includes("..")) return void 0;
7312
+ const market = marketSource.trim();
7313
+ if (market.startsWith("github:") || market.startsWith("git+")) {
7314
+ const [base = "", fragment] = market.split("#", 2);
7315
+ const prefix = fragment?.startsWith("subdir=") ? `${fragment.slice("subdir=".length)}/` : "";
7316
+ return `${base}#subdir=${prefix}${rel}`;
7218
7317
  }
7219
- try {
7220
- unlinkSync(dest);
7221
- } catch {
7222
- rmdirSync(dest);
7318
+ if (market.startsWith("https://") || market.startsWith("http://")) {
7319
+ return `${market.replace(/\/[^/]*$/, "")}/${rel}`;
7223
7320
  }
7321
+ return join(marketDir(market), rel);
7224
7322
  }
7225
- async function fetchInto(source, staging, opts) {
7226
- if (source.type === "local") return fetchLocal(source);
7227
- if (source.type === "git") return fetchGit(source, staging, opts);
7228
- if (source.type === "npm") return fetchNpm(source, staging, opts);
7229
- if (source.type === "host") return fetchFromHost(source, staging, opts);
7230
- if (source.localFile) return fetchLocalArchive(source, staging);
7231
- return fetchArchive(source, staging, opts);
7232
- }
7233
- async function fetchLocalArchive(source, staging) {
7234
- if (!existsSync(source.target)) {
7235
- return { ok: false, reason: t("plugin_install.archive_missing", { path: source.target }) };
7323
+ function findMarketplaceEntryByName(name, statePath) {
7324
+ for (const record of readMarketplaces(statePath).records) {
7325
+ const entry = record.catalog.plugins.find((plugin) => plugin.name === name);
7326
+ if (!entry) continue;
7327
+ const source = resolveEntrySource(entry.source, record.source);
7328
+ if (!source) continue;
7329
+ return { marketplace: record.name, source };
7236
7330
  }
7237
- mkdirSync(staging, { recursive: true });
7238
- return extractArchive(source.target, staging);
7331
+ return void 0;
7239
7332
  }
7240
- async function fetchFromHost(source, staging, opts) {
7241
- const ask = opts.fetchFromHost;
7242
- if (!ask) {
7243
- return { ok: false, reason: t("plugin_install.host_fetch_unavailable") };
7244
- }
7245
- opts.onProgress?.(t("plugin_install.host_fetching", { target: source.target }));
7246
- mkdirSync(staging, { recursive: true });
7247
- return withDownloadDir(async (dl) => {
7248
- let answer;
7333
+ async function fetchCatalog(source) {
7334
+ const input = source.trim();
7335
+ if (!input) return { ok: false, reason: t("plugin_marketplace.source_empty") };
7336
+ const local = localCatalogPath(input);
7337
+ if (local) {
7338
+ if (!existsSync(local)) {
7339
+ return { ok: false, reason: t("plugin_marketplace.local_missing", { path: local }) };
7340
+ }
7249
7341
  try {
7250
- answer = await ask({ target: source.target, destDir: dl });
7342
+ return parseCatalog(readFileSync(local, "utf-8"), local);
7251
7343
  } catch (err) {
7252
- return {
7253
- ok: false,
7254
- reason: t("plugin_install.host_fetch_threw", {
7255
- reason: (err instanceof Error ? err.message : String(err)).split("\n")[0] ?? ""
7256
- })
7257
- };
7258
- }
7259
- if (!answer.ok) {
7260
- return {
7261
- ok: false,
7262
- reason: answer.reason ?? t("plugin_install.host_fetch_failed", { target: source.target })
7263
- };
7344
+ return { ok: false, reason: t("plugin_marketplace.read_failed", { reason: first(err) }) };
7264
7345
  }
7265
- if (!existsSync(answer.archivePath)) {
7346
+ }
7347
+ const url = catalogUrl(input);
7348
+ if (!url) {
7349
+ return {
7350
+ ok: false,
7351
+ reason: t("plugin_marketplace.bad_url", { input, file: MARKETPLACE_FILE })
7352
+ };
7353
+ }
7354
+ try {
7355
+ const response = await fetch(url, { redirect: "follow" });
7356
+ if (!response.ok) {
7357
+ const vars = { url, status: response.status };
7266
7358
  return {
7267
7359
  ok: false,
7268
- reason: t("plugin_install.host_fetch_no_file", { path: answer.archivePath })
7360
+ reason: looksPrivate(response.status) ? t("marketplace.fetch_failed_private", vars) : t("marketplace.fetch_failed", vars)
7269
7361
  };
7270
7362
  }
7271
- return extractArchive(answer.archivePath, staging);
7272
- });
7273
- }
7274
- function fetchLocal(source) {
7275
- if (!existsSync(source.target)) {
7276
- return { ok: false, reason: t("plugin_install.local_missing", { path: source.target }) };
7363
+ return parseCatalog(await response.text(), url);
7364
+ } catch (err) {
7365
+ return { ok: false, reason: t("marketplace.fetch_error", { url, reason: first(err) }) };
7277
7366
  }
7278
- return { ok: true };
7279
7367
  }
7280
- async function fetchGit(source, staging, opts) {
7281
- opts.onProgress?.(t("plugin_install.cloning", { target: source.target }));
7282
- const result = await runCommand("git", ["clone", "--depth", "1", source.target, staging]);
7283
- if (!result.ok) {
7284
- return { ok: false, reason: t("plugin_install.clone_failed", { detail: result.detail }) };
7368
+ function looksPrivate(status) {
7369
+ return status === 401 || status === 403 || status === 404;
7370
+ }
7371
+ function catalogUrl(input) {
7372
+ if (input.startsWith("github:")) {
7373
+ const slug = input.slice("github:".length);
7374
+ if (!/^[\w.-]+\/[\w.-]+$/.test(slug)) return void 0;
7375
+ return `https://raw.githubusercontent.com/${slug}/HEAD/${MARKETPLACE_FILE}`;
7285
7376
  }
7286
- rmSync(join(staging, ".git"), { recursive: true, force: true });
7287
- return { ok: true };
7377
+ if (!input.startsWith("https://") && !input.startsWith("http://")) return void 0;
7378
+ return input.endsWith(".json") ? input : `${input.replace(/\/+$/, "")}/${MARKETPLACE_FILE}`;
7288
7379
  }
7289
- async function fetchNpm(source, staging, opts) {
7290
- opts.onProgress?.(t("plugin_install.npm_fetching", { target: source.target }));
7291
- mkdirSync(staging, { recursive: true });
7292
- return withDownloadDir(async (dl) => {
7293
- const packed = await runCommand(
7294
- "npm",
7295
- ["pack", source.target, "--pack-destination", dl, "--silent"],
7296
- { cwd: dl, windowsShell: true }
7297
- );
7298
- if (!packed.ok) {
7299
- return { ok: false, reason: t("plugin_install.npm_pack_failed", { detail: packed.detail }) };
7300
- }
7301
- const tarball = packed.stdout.trim().split("\n").pop()?.trim();
7302
- if (!tarball) return { ok: false, reason: t("plugin_install.npm_no_tarball") };
7303
- return extractArchive(isAbsolute(tarball) ? tarball : join(dl, tarball), staging);
7304
- });
7380
+ function localCatalogPath(input) {
7381
+ if (/^[a-z][a-z0-9+.-]+:/i.test(input) || input.startsWith("//")) return void 0;
7382
+ const abs = isAbsolute(input) ? input : resolve(input);
7383
+ if (existsSync(abs) && statSync(abs).isDirectory()) return join(abs, MARKETPLACE_FILE);
7384
+ return abs;
7305
7385
  }
7306
- async function fetchArchive(source, staging, opts) {
7307
- opts.onProgress?.(t("plugin_install.downloading", { target: source.target }));
7308
- mkdirSync(staging, { recursive: true });
7309
- let bytes;
7386
+ function marketDir(market) {
7387
+ const abs = isAbsolute(market) ? market : resolve(market);
7388
+ if (existsSync(abs) && statSync(abs).isDirectory()) return abs;
7389
+ return abs.replace(/[/\\][^/\\]*$/, "");
7390
+ }
7391
+ function parseCatalog(body2, where) {
7392
+ let raw;
7310
7393
  try {
7311
- const response = await fetch(source.target, { redirect: "follow" });
7312
- if (!response.ok) {
7313
- return {
7314
- ok: false,
7315
- reason: t("plugin_install.download_http", {
7316
- status: response.status,
7317
- statusText: response.statusText
7318
- })
7319
- };
7320
- }
7321
- bytes = Buffer.from(await response.arrayBuffer());
7394
+ raw = JSON.parse(body2);
7322
7395
  } catch (err) {
7323
7396
  return {
7324
7397
  ok: false,
7325
- reason: t("plugin_install.download_failed", {
7326
- message: err instanceof Error ? err.message : String(err)
7327
- })
7398
+ reason: t("plugin_marketplace.catalog_bad_json", { where, reason: first(err) })
7328
7399
  };
7329
7400
  }
7330
- if (source.sha256) {
7331
- const actual = createHash("sha256").update(bytes).digest("hex");
7332
- if (actual !== source.sha256) {
7333
- return {
7334
- ok: false,
7335
- reason: [
7336
- t("plugin_install.sha_mismatch"),
7337
- t("plugin_install.sha_expected", { expected: source.sha256 }),
7338
- t("plugin_install.sha_actual", { actual })
7339
- ].join("\n")
7340
- };
7341
- }
7401
+ const checked = parseLenient(CatalogSchema.optional(), raw, void 0);
7402
+ if (!checked.value) {
7403
+ const detail = checked.issues.map((i) => t("plugin_marketplace.issue", { path: i.path, message: i.message })).join(t("plugin_marketplace.catalog_issue_sep"));
7404
+ return { ok: false, reason: t("plugin_marketplace.catalog_invalid", { where, detail }) };
7342
7405
  }
7343
- return withDownloadDir(async (dl) => {
7344
- const archive = join(dl, `download${archiveExt(source.target)}`);
7345
- writeFileSync(archive, bytes);
7346
- return extractArchive(archive, staging);
7347
- });
7406
+ return { ok: true, catalog: checked.value };
7348
7407
  }
7349
- function archiveExt(url) {
7350
- const path = (url.split("?")[0] ?? "").toLowerCase();
7351
- if (path.endsWith(".tgz")) return ".tgz";
7352
- if (path.endsWith(".tar.gz")) return ".tar.gz";
7353
- return ".zip";
7408
+ function first(err) {
7409
+ const message = err instanceof Error ? err.message : String(err);
7410
+ return message.split("\n")[0] ?? t("plugin_marketplace.unknown_error");
7354
7411
  }
7355
- async function withDownloadDir(fn) {
7356
- const dir = mkdtempSync(join(tmpdir(), "epoch-plugin-dl-"));
7357
- try {
7358
- return await fn(dir);
7359
- } finally {
7360
- rmSync(dir, { recursive: true, force: true });
7412
+ var NPM_SPEC = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*(?:@[a-zA-Z0-9.^~*+-]+)?$/;
7413
+ var REMOTE_URL = /^https?:\/\/[^\s'"\\^|<>]+$/;
7414
+ function resolveSource(raw) {
7415
+ const input = raw.trim();
7416
+ if (!input) return void 0;
7417
+ if (input.startsWith("github:")) {
7418
+ const [slug = "", fragment] = input.slice("github:".length).split("#", 2);
7419
+ if (!/^[\w.-]+\/[\w.-]+$/.test(slug)) return void 0;
7420
+ const sub = subdirOf(fragment);
7421
+ if (!sub.ok) return void 0;
7422
+ return {
7423
+ type: "git",
7424
+ raw: input,
7425
+ target: `https://github.com/${slug}.git`,
7426
+ ...sub.subdir ? { subdir: sub.subdir } : {}
7427
+ };
7428
+ }
7429
+ if (input.startsWith("git+")) {
7430
+ const [url = "", fragment] = input.slice("git+".length).split("#", 2);
7431
+ if (!REMOTE_URL.test(url)) return void 0;
7432
+ const sub = subdirOf(fragment);
7433
+ if (!sub.ok) return void 0;
7434
+ return { type: "git", raw: input, target: url, ...sub.subdir ? { subdir: sub.subdir } : {} };
7435
+ }
7436
+ if (input.startsWith("npm:")) {
7437
+ const pkg = input.slice("npm:".length);
7438
+ if (!NPM_SPEC.test(pkg)) return void 0;
7439
+ return { type: "npm", raw: input, target: pkg };
7440
+ }
7441
+ if (input.startsWith("host:")) {
7442
+ const opaque = input.slice("host:".length).trim();
7443
+ if (!opaque) return void 0;
7444
+ return { type: "host", raw: input, target: opaque };
7445
+ }
7446
+ if (input.startsWith("https://") || input.startsWith("http://")) {
7447
+ const [url = "", fragment] = input.split("#", 2);
7448
+ if (!REMOTE_URL.test(url)) return void 0;
7449
+ const sha = fragment?.startsWith("sha256=") ? fragment.slice("sha256=".length) : void 0;
7450
+ if (isArchiveUrl(url)) {
7451
+ if (sha !== void 0 && !/^[a-f0-9]{64}$/i.test(sha)) return void 0;
7452
+ return {
7453
+ type: "archive",
7454
+ raw: input,
7455
+ target: url,
7456
+ ...sha ? { sha256: sha.toLowerCase() } : {}
7457
+ };
7458
+ }
7459
+ return { type: "git", raw: input, target: url };
7460
+ }
7461
+ const abs = isAbsolute(input) ? input : resolve(input);
7462
+ if (isArchiveUrl(abs)) {
7463
+ return { type: "archive", raw: input, target: abs, localFile: true };
7361
7464
  }
7465
+ return { type: "local", raw: input, target: abs };
7362
7466
  }
7363
- // src/plugin/root-token.ts
7364
- var PLUGIN_ROOT_TOKEN = "${EPOCH_PLUGIN_ROOT}";
7365
- var PLUGIN_ROOT_ENV = "EPOCH_PLUGIN_ROOT";
7366
- function mentionsPluginRoot(text) {
7367
- return text.includes(PLUGIN_ROOT_TOKEN);
7467
+ function isArchiveUrl(url) {
7468
+ return isWirePluginArchiveName(url);
7368
7469
  }
7369
- function substitutePluginRoot(text, root) {
7370
- return text.split(PLUGIN_ROOT_TOKEN).join(root);
7470
+ function subdirOf(fragment) {
7471
+ if (fragment === void 0 || fragment === "") return { ok: true };
7472
+ if (!fragment.startsWith("subdir=")) return { ok: false };
7473
+ const rel = fragment.slice("subdir=".length).replace(/\/+$/, "");
7474
+ if (!/^[\w.-]+(?:\/[\w.-]+)*$/.test(rel)) return { ok: false };
7475
+ if (rel.split("/").includes("..")) return { ok: false };
7476
+ return { ok: true, subdir: rel };
7371
7477
  }
7372
- async function uninstallPlugin(name, opts) {
7373
- const removed = await new PluginStore(opts.statePath).mutate((records) => {
7374
- const hit = records.find((r) => r.name === name);
7375
- if (!hit) return void 0;
7376
- return { records: records.filter((r) => r.name !== name), result: hit };
7377
- });
7378
- if (!removed) return { ok: false, reason: t("plugin_lifecycle.not_installed", { name }) };
7379
- if (removed.linked) {
7380
- rmSync(removed.path, { force: true });
7381
- return { ok: true, message: t("plugin_lifecycle.uninstalled_link", { name }) };
7382
- }
7383
- rmSync(removed.path, { recursive: true, force: true });
7384
- return { ok: true, message: t("plugin_lifecycle.uninstalled", { name }) };
7478
+ function pluginRoot(source, staging) {
7479
+ return source.subdir ? join(staging, source.subdir) : staging;
7385
7480
  }
7386
- async function setPluginEnabled(name, enabled, opts) {
7387
- const changed = await new PluginStore(opts.statePath).mutate((records) => {
7388
- const hit = records.find((r) => r.name === name);
7389
- if (!hit) return void 0;
7481
+ // src/plugin/install.ts
7482
+ async function installPlugin(source, opts) {
7483
+ const resolved = resolveSource(source);
7484
+ if (!resolved) {
7390
7485
  return {
7391
- records: records.map((r) => r.name === name ? { ...r, enabled } : r),
7392
- result: hit.enabled !== enabled
7486
+ ok: false,
7487
+ reason: t("plugin_install.bad_source", { source })
7393
7488
  };
7394
- });
7395
- if (changed === void 0) {
7396
- return { ok: false, reason: t("plugin_lifecycle.not_installed", { name }) };
7397
7489
  }
7398
- const verb = enabled ? t("plugin_lifecycle.verb_enable") : t("plugin_lifecycle.verb_disable");
7399
- return {
7400
- ok: true,
7401
- message: changed ? t("plugin_lifecycle.toggled", { verb, name }) : t("plugin_lifecycle.already", { name, verb })
7402
- };
7403
- }
7404
- async function updatePlugin(name, opts) {
7405
- const existing = new PluginStore(opts.statePath).read().records.find((r) => r.name === name);
7406
- if (!existing) return { ok: false, reason: t("plugin_lifecycle.not_installed", { name }) };
7407
- if (existing.linked) {
7408
- return {
7409
- ok: true,
7410
- message: t("plugin_lifecycle.linked_no_update", { name, source: existing.source })
7411
- };
7490
+ mkdirSync(opts.pluginsDir, { recursive: true });
7491
+ const staging = join(opts.pluginsDir, `.staging-${randomUUID().slice(0, 8)}`);
7492
+ try {
7493
+ return await runInstall(resolved, staging, opts);
7494
+ } finally {
7495
+ rmSync(staging, { recursive: true, force: true });
7412
7496
  }
7413
- return installPlugin(existing.source, {
7414
- ...opts,
7415
- replace: name,
7416
- ...existing.marketplace ? { marketplace: existing.marketplace } : {}
7417
- });
7418
7497
  }
7419
- var MARKETPLACE_FILE = "epoch-marketplace.json";
7420
- var NAME_PATTERN = PLUGIN_NAME_PATTERN;
7421
- var EntryCategoryInput = z.union([z.string().min(1), z.array(z.string().min(1))]).optional().catch(void 0);
7422
- var EntryShape = z.object({
7423
- name: z.string().regex(NAME_PATTERN),
7424
- description: z.string().min(1).optional(),
7425
- source: z.string().min(1),
7426
- category: EntryCategoryInput,
7427
- keywords: z.array(z.string().min(1)).optional(),
7428
- author: PluginAuthorSchema.optional(),
7429
- downloads: z.number().int().nonnegative().optional(),
7430
- likes: z.number().int().nonnegative().optional(),
7431
- displayName: z.string().min(1).optional(),
7432
- icon: z.string().min(1).optional(),
7433
- version: z.string().min(1).optional(),
7434
- changelog: z.string().min(1).optional(),
7435
- size: z.number().int().positive().optional(),
7436
- homepage: z.string().min(1).optional(),
7437
- visibility: z.enum(PLUGIN_VISIBILITY).optional().catch(void 0),
7438
- status: z.enum(["listed", "reviewing", "rejected", "offline"]).optional().catch(void 0)
7439
- });
7440
- var EntrySchema = EntryShape.transform(({ category, ...rest }) => {
7441
- const picked = category === void 0 ? [] : typeof category === "string" ? [category] : category;
7442
- return { ...rest, ...picked.length === 0 ? {} : { category: [...picked] } };
7443
- });
7444
- var MARKETPLACE_DISPLAY_FIELDS = [
7445
- "status",
7446
- "downloads",
7447
- "likes",
7448
- "author",
7449
- "category",
7450
- "visibility",
7451
- "icon",
7452
- "homepage",
7453
- "size"
7454
- ];
7455
- var DisplaySchema = z.object({
7456
- fields: z.array(z.string().min(1)).optional()
7457
- });
7458
- var CategoryNodeSchema = z.lazy(
7459
- () => z.object({
7460
- id: z.string().min(1),
7461
- label: z.string().min(1).optional(),
7462
- children: z.array(CategoryNodeSchema).optional()
7463
- })
7464
- );
7465
- var CategoriesSchema = z.array(CategoryNodeSchema).refine(hasUniqueCategoryIds, { message: "category ids must be unique across the whole tree" }).optional().catch(void 0);
7466
- function hasUniqueCategoryIds(nodes) {
7467
- const seen = /* @__PURE__ */ new Set();
7468
- const walk3 = (list) => list.every((node) => {
7469
- if (seen.has(node.id)) return false;
7470
- seen.add(node.id);
7471
- return node.children === void 0 || walk3(node.children);
7472
- });
7473
- return walk3(nodes);
7474
- }
7475
- var CatalogSchema = z.object({
7476
- name: z.string().regex(NAME_PATTERN),
7477
- description: z.string().min(1).optional(),
7478
- owner: z.object({ name: z.string().min(1), url: z.string().min(1).optional() }).optional(),
7479
- display: DisplaySchema.optional(),
7480
- categories: CategoriesSchema,
7481
- plugins: z.array(EntrySchema)
7482
- });
7483
- var RecordSchema2 = z.object({
7484
- name: z.string().regex(NAME_PATTERN),
7485
- source: z.string().min(1),
7486
- addedAt: z.number(),
7487
- catalog: CatalogSchema
7488
- });
7489
- var StateSchema2 = z.object({
7490
- version: z.literal(1).optional(),
7491
- marketplaces: z.array(RecordSchema2)
7492
- });
7493
- function projectEntryForDisplay(entry, display) {
7494
- const allowed = display?.fields;
7495
- if (allowed === void 0) return entry;
7496
- const out = { ...entry };
7497
- for (const field of MARKETPLACE_DISPLAY_FIELDS) {
7498
- if (!displayAllows(display, field)) delete out[field];
7498
+ async function runInstall(resolved, staging, opts) {
7499
+ const rootStaging = join(staging, "root");
7500
+ const fetched = await fetchInto(resolved, rootStaging, opts);
7501
+ if (!fetched.ok) return { ok: false, reason: fetched.reason };
7502
+ const readDir = resolved.type === "local" ? resolved.target : pluginRoot(resolved, rootStaging);
7503
+ const read = readPluginManifest(readDir);
7504
+ const manifest = read.manifest;
7505
+ if (!manifest) {
7506
+ const detail = read.issues.map((i) => t("plugin_install.issue_line", { path: i.path, message: i.message })).join("\n");
7507
+ return { ok: false, reason: t("plugin_install.manifest_unusable", { detail: `
7508
+ ${detail}` }) };
7499
7509
  }
7500
- return out;
7501
- }
7502
- function displayAllows(display, field) {
7503
- const allowed = display?.fields;
7504
- if (allowed === void 0) return true;
7505
- return allowed.includes(field);
7506
- }
7507
- function readMarketplaces(statePath) {
7508
- if (!existsSync(statePath)) return { records: [], issues: [] };
7509
- let raw;
7510
- try {
7511
- raw = JSON.parse(readFileSync(statePath, "utf-8"));
7512
- } catch (err) {
7510
+ const taken = new PluginStore(opts.statePath).read().records.find((r) => r.name === manifest.name);
7511
+ const conflict = taken && taken.name !== opts.replace ? taken : void 0;
7512
+ const current = opts.epochVersion ?? currentEpochVersion();
7513
+ const version = checkEpochVersion(manifest.epochVersion, current);
7514
+ const versionUnmet = !version.ok && manifest.epochVersion ? { required: manifest.epochVersion, current, reason: version.reason } : void 0;
7515
+ const closure = await resolveClosure({
7516
+ root: manifest.name,
7517
+ declared: manifest.dependencies ?? [],
7518
+ installed: new Map(
7519
+ new PluginStore(opts.statePath).read().records.map((r) => [r.name, r.path])
7520
+ ),
7521
+ staging,
7522
+ opts
7523
+ });
7524
+ const preview3 = {
7525
+ manifest,
7526
+ source: resolved,
7527
+ inventory: scanPluginDir(readDir, manifest.name),
7528
+ ...conflict ? { conflict } : {},
7529
+ ...versionUnmet ? { versionUnmet } : {},
7530
+ ...closure.report.length > 0 ? { dependencies: closure.report } : {}
7531
+ };
7532
+ if (opts.replace !== void 0 && manifest.name !== opts.replace) {
7513
7533
  return {
7514
- records: [],
7515
- issues: [
7516
- { path: statePath, message: t("plugin_marketplace.bad_json", { reason: first(err) }) }
7517
- ]
7534
+ ok: false,
7535
+ preview: preview3,
7536
+ reason: t("plugin_install.renamed", { found: manifest.name, expected: opts.replace })
7518
7537
  };
7519
7538
  }
7520
- const checked = parseLenient(StateSchema2.optional(), raw, void 0);
7521
- if (!checked.value) {
7539
+ if (conflict) {
7522
7540
  return {
7523
- records: [],
7524
- issues: checked.issues.map((i) => ({
7525
- path: statePath,
7526
- message: t("plugin_marketplace.issue", { path: i.path, message: i.message })
7527
- }))
7541
+ ok: false,
7542
+ preview: preview3,
7543
+ reason: t("plugin_install.already_installed", {
7544
+ name: conflict.name,
7545
+ version: conflict.version,
7546
+ source: conflict.source
7547
+ })
7528
7548
  };
7529
7549
  }
7530
- return { records: checked.value.marketplaces, issues: [] };
7531
- }
7532
- async function mutate(statePath, fn) {
7533
- const release = await acquireStateLock(statePath);
7534
- try {
7535
- const next = fn(readMarketplaces(statePath).records);
7536
- if (!next) return false;
7537
- writeStateFile(statePath, JSON.stringify({ version: 1, marketplaces: next }, null, 2));
7538
- return true;
7539
- } finally {
7540
- release();
7550
+ if (opts.confirm && !await opts.confirm(preview3)) {
7551
+ return { ok: false, reason: t("plugin.install_cancelled"), cancelled: true, preview: preview3 };
7541
7552
  }
7542
- }
7543
- async function addMarketplace(source, opts) {
7544
- const fetched = await fetchCatalog(source);
7545
- if (!fetched.ok) return fetched;
7546
- const record = {
7547
- name: fetched.catalog.name,
7548
- source: source.trim(),
7549
- addedAt: Date.now(),
7550
- catalog: fetched.catalog
7551
- };
7552
- const written = await mutate(
7553
- opts.statePath,
7554
- (records) => records.some((r) => r.name === record.name) ? void 0 : [...records, record].sort((a, b) => a.name.localeCompare(b.name))
7553
+ const records = await commitAll(
7554
+ [
7555
+ {
7556
+ name: manifest.name,
7557
+ version: manifest.version,
7558
+ source: resolved,
7559
+ stagingDir: rootStaging,
7560
+ ...opts.marketplace ? { marketplace: opts.marketplace } : {}
7561
+ },
7562
+ ...closure.plans.map((plan) => ({
7563
+ name: plan.name,
7564
+ version: plan.manifest.version,
7565
+ source: plan.source,
7566
+ stagingDir: plan.stagingDir,
7567
+ ...plan.marketplace ? { marketplace: plan.marketplace } : {}
7568
+ }))
7569
+ ],
7570
+ opts
7555
7571
  );
7556
- return written ? { ok: true, record } : {
7572
+ const record = records?.find((r) => r.name === manifest.name);
7573
+ return record ? { ok: true, record, preview: preview3 } : {
7557
7574
  ok: false,
7558
- reason: t("plugin_marketplace.already_added", { name: record.name })
7575
+ preview: preview3,
7576
+ reason: t("plugin_install.raced", { name: manifest.name })
7559
7577
  };
7560
7578
  }
7561
- async function removeMarketplace(name, opts) {
7562
- const written = await mutate(
7563
- opts.statePath,
7564
- (records) => records.some((r) => r.name === name) ? records.filter((r) => r.name !== name) : void 0
7565
- );
7566
- return written ? { ok: true } : { ok: false, reason: t("plugin_marketplace.not_added", { name }) };
7567
- }
7568
- async function updateMarketplace(name, opts) {
7569
- const existing = readMarketplaces(opts.statePath).records.find((r) => r.name === name);
7570
- if (!existing) return { ok: false, reason: t("plugin_marketplace.not_added", { name }) };
7571
- const fetched = await fetchCatalog(existing.source);
7572
- if (!fetched.ok) return fetched;
7573
- if (fetched.catalog.name !== name) {
7574
- return {
7575
- ok: false,
7576
- reason: t("plugin_marketplace.renamed", {
7577
- source: existing.source,
7578
- name: fetched.catalog.name
7579
- })
7580
- };
7579
+ var DEPENDENCY_LIMIT = 32;
7580
+ async function commitAll(entries2, opts) {
7581
+ const records = [];
7582
+ for (const entry of entries2) {
7583
+ const dest = join(opts.pluginsDir, entry.name);
7584
+ removeInstallDest(dest);
7585
+ let linked = entry.source.type === "local";
7586
+ if (linked) {
7587
+ try {
7588
+ symlinkSync(entry.source.target, dest, "junction");
7589
+ } catch {
7590
+ opts.onProgress?.(t("plugin_install.symlink_failed"));
7591
+ cpSync(entry.source.target, dest, { recursive: true });
7592
+ linked = false;
7593
+ }
7594
+ } else {
7595
+ renameSync(pluginRoot(entry.source, entry.stagingDir), dest);
7596
+ }
7597
+ records.push({
7598
+ name: entry.name,
7599
+ version: entry.version,
7600
+ source: entry.source.raw,
7601
+ sourceType: entry.source.type,
7602
+ path: dest,
7603
+ linked,
7604
+ enabled: true,
7605
+ installedAt: Date.now(),
7606
+ ...entry.marketplace ? { marketplace: entry.marketplace } : {},
7607
+ ...entry.source.sha256 ? { checksum: `sha256:${entry.source.sha256}` } : {}
7608
+ });
7581
7609
  }
7582
- const record = { ...existing, catalog: fetched.catalog };
7583
- await mutate(opts.statePath, (records) => records.map((r) => r.name === name ? record : r));
7584
- return { ok: true, record };
7610
+ return new PluginStore(opts.statePath).mutate((existing) => {
7611
+ for (const record of records) {
7612
+ const taken = existing.some((r) => r.name === record.name);
7613
+ if (taken && opts.replace !== record.name) return void 0;
7614
+ }
7615
+ let next = existing;
7616
+ for (const record of records) {
7617
+ const taken = next.some((r) => r.name === record.name);
7618
+ next = taken ? next.map((r) => r.name === record.name ? record : r) : [...next, record];
7619
+ }
7620
+ return { records: next, result: records };
7621
+ });
7585
7622
  }
7586
- function searchMarketplaces(keyword, statePath) {
7587
- const needle = keyword.trim().toLowerCase();
7588
- const hits = [];
7589
- for (const record of readMarketplaces(statePath).records) {
7590
- for (const entry of record.catalog.plugins) {
7591
- const haystack = [
7592
- entry.name,
7593
- entry.description ?? "",
7594
- ...entry.category ?? [],
7595
- entry.author?.name ?? "",
7596
- ...entry.keywords ?? []
7597
- ].join(" ").toLowerCase();
7598
- if (needle && !haystack.includes(needle)) continue;
7599
- hits.push({
7600
- marketplace: record.name,
7601
- entry: projectEntryForDisplay(entry, record.catalog.display),
7602
- ref: `${record.name}/${entry.name}`
7623
+ async function resolveClosure(input) {
7624
+ const { opts, staging } = input;
7625
+ const seen = /* @__PURE__ */ new Set([input.root]);
7626
+ const queue = input.declared.map((name) => ({
7627
+ name,
7628
+ requiredBy: input.root
7629
+ }));
7630
+ const report3 = [];
7631
+ const plans = [];
7632
+ while (queue.length > 0) {
7633
+ const next = queue.shift();
7634
+ if (!next) break;
7635
+ const { name, requiredBy } = next;
7636
+ if (seen.has(name)) continue;
7637
+ seen.add(name);
7638
+ const installedAt = input.installed.get(name);
7639
+ if (installedAt !== void 0) {
7640
+ report3.push({
7641
+ name,
7642
+ status: "installed",
7643
+ requiredBy,
7644
+ ...installedDisplayName(installedAt)
7603
7645
  });
7646
+ continue;
7647
+ }
7648
+ if (plans.length >= DEPENDENCY_LIMIT) {
7649
+ report3.push({
7650
+ name,
7651
+ status: "unresolved",
7652
+ requiredBy,
7653
+ reason: t("plugin_install.dep_too_many", { limit: DEPENDENCY_LIMIT, root: input.root })
7654
+ });
7655
+ continue;
7656
+ }
7657
+ const located = await locateDependency(name, opts);
7658
+ if (!located) {
7659
+ report3.push({
7660
+ name,
7661
+ status: "unresolved",
7662
+ requiredBy,
7663
+ reason: t("plugin_install.dep_no_source", { name })
7664
+ });
7665
+ continue;
7666
+ }
7667
+ const source = resolveSource(located.source);
7668
+ if (!source) {
7669
+ report3.push({
7670
+ name,
7671
+ status: "unresolved",
7672
+ requiredBy,
7673
+ source: located.source,
7674
+ reason: t("plugin_install.dep_bad_source", { name, source: located.source })
7675
+ });
7676
+ continue;
7677
+ }
7678
+ opts.onProgress?.(t("plugin_install.dep_fetching", { name, source: located.source }));
7679
+ const stagingDir = join(staging, `dep-${plans.length}`);
7680
+ const fetched = await fetchInto(source, stagingDir, opts);
7681
+ if (!fetched.ok) {
7682
+ report3.push({
7683
+ name,
7684
+ status: "unresolved",
7685
+ requiredBy,
7686
+ source: located.source,
7687
+ reason: fetched.reason
7688
+ });
7689
+ continue;
7690
+ }
7691
+ const readDir = source.type === "local" ? source.target : pluginRoot(source, stagingDir);
7692
+ const read = readPluginManifest(readDir);
7693
+ const manifest = read.manifest;
7694
+ if (!manifest) {
7695
+ report3.push({
7696
+ name,
7697
+ status: "unresolved",
7698
+ requiredBy,
7699
+ source: located.source,
7700
+ reason: t("plugin_install.dep_bad_manifest", {
7701
+ name,
7702
+ detail: read.issues.map((issue) => issue.message).join("; ")
7703
+ })
7704
+ });
7705
+ continue;
7706
+ }
7707
+ if (manifest.name !== name) {
7708
+ report3.push({
7709
+ name,
7710
+ status: "unresolved",
7711
+ requiredBy,
7712
+ source: located.source,
7713
+ reason: t("plugin_install.dep_name_mismatch", { name, found: manifest.name })
7714
+ });
7715
+ continue;
7716
+ }
7717
+ const current = opts.epochVersion ?? currentEpochVersion();
7718
+ const version = checkEpochVersion(manifest.epochVersion, current);
7719
+ const versionUnmet = !version.ok && manifest.epochVersion ? { required: manifest.epochVersion, current, reason: version.reason } : void 0;
7720
+ report3.push({
7721
+ name,
7722
+ status: "plan",
7723
+ requiredBy,
7724
+ source: located.source,
7725
+ ...manifest.displayName ? { displayName: manifest.displayName } : {},
7726
+ manifest,
7727
+ inventory: scanPluginDir(readDir, manifest.name),
7728
+ ...versionUnmet ? { versionUnmet } : {}
7729
+ });
7730
+ plans.push({
7731
+ name: manifest.name,
7732
+ source,
7733
+ stagingDir,
7734
+ manifest,
7735
+ ...located.marketplace ? { marketplace: located.marketplace } : {}
7736
+ });
7737
+ for (const nested of manifest.dependencies ?? []) {
7738
+ queue.push({ name: nested, requiredBy: manifest.name });
7604
7739
  }
7605
7740
  }
7606
- return hits;
7741
+ return { report: report3, plans };
7607
7742
  }
7608
- function resolveMarketplaceRef(input, statePath) {
7609
- const parts = input.trim().split("/");
7610
- if (parts.length !== 2) return void 0;
7611
- const [market = "", plugin = ""] = parts;
7612
- if (!NAME_PATTERN.test(market) || !NAME_PATTERN.test(plugin)) return void 0;
7613
- const record = readMarketplaces(statePath).records.find((r) => r.name === market);
7614
- if (!record) {
7615
- return {
7616
- ok: false,
7617
- reason: t("plugin_marketplace.market_not_added", { market, input })
7618
- };
7743
+ function installedDisplayName(dir) {
7744
+ const displayName = readPluginManifest(dir).manifest?.displayName;
7745
+ return displayName ? { displayName } : {};
7746
+ }
7747
+ async function locateDependency(name, opts) {
7748
+ const local = opts.marketplacesStatePath ? findMarketplaceEntryByName(name, opts.marketplacesStatePath) : void 0;
7749
+ if (local) return { source: local.source, marketplace: local.marketplace };
7750
+ if (!opts.resolveDependency) return void 0;
7751
+ try {
7752
+ const answer = await opts.resolveDependency(name);
7753
+ return answer ? { source: answer } : void 0;
7754
+ } catch {
7755
+ return void 0;
7619
7756
  }
7620
- const entry = record.catalog.plugins.find((p) => p.name === plugin);
7621
- if (!entry) {
7622
- return {
7623
- ok: false,
7624
- reason: t("plugin_marketplace.plugin_not_in_market", { market, plugin })
7625
- };
7757
+ }
7758
+ function removeInstallDest(dest) {
7759
+ let link = false;
7760
+ try {
7761
+ link = lstatSync(dest).isSymbolicLink();
7762
+ } catch {
7626
7763
  }
7627
- const source = resolveEntrySource(entry.source, record.source);
7628
- if (!source) {
7629
- return {
7630
- ok: false,
7631
- reason: t("plugin_marketplace.entry_unresolvable", {
7632
- market,
7633
- plugin,
7634
- source: entry.source
7635
- })
7636
- };
7764
+ if (!link) {
7765
+ rmSync(dest, { recursive: true, force: true });
7766
+ return;
7637
7767
  }
7638
- return { ok: true, ref: { marketplace: market, source } };
7768
+ try {
7769
+ unlinkSync(dest);
7770
+ } catch {
7771
+ rmdirSync(dest);
7772
+ }
7773
+ }
7774
+ async function fetchInto(source, staging, opts) {
7775
+ if (source.type === "local") return fetchLocal(source);
7776
+ if (source.type === "git") return fetchGit(source, staging, opts);
7777
+ if (source.type === "npm") return fetchNpm(source, staging, opts);
7778
+ if (source.type === "host") return fetchFromHost(source, staging, opts);
7779
+ if (source.localFile) return fetchLocalArchive(source, staging);
7780
+ return fetchArchive(source, staging, opts);
7781
+ }
7782
+ async function fetchLocalArchive(source, staging) {
7783
+ if (!existsSync(source.target)) {
7784
+ return { ok: false, reason: t("plugin_install.archive_missing", { path: source.target }) };
7785
+ }
7786
+ mkdirSync(staging, { recursive: true });
7787
+ return extractArchive(source.target, staging);
7788
+ }
7789
+ async function fetchFromHost(source, staging, opts) {
7790
+ const ask = opts.fetchFromHost;
7791
+ if (!ask) {
7792
+ return { ok: false, reason: t("plugin_install.host_fetch_unavailable") };
7793
+ }
7794
+ opts.onProgress?.(t("plugin_install.host_fetching", { target: source.target }));
7795
+ mkdirSync(staging, { recursive: true });
7796
+ return withDownloadDir(async (dl) => {
7797
+ let answer;
7798
+ try {
7799
+ answer = await ask({ target: source.target, destDir: dl });
7800
+ } catch (err) {
7801
+ return {
7802
+ ok: false,
7803
+ reason: t("plugin_install.host_fetch_threw", {
7804
+ reason: (err instanceof Error ? err.message : String(err)).split("\n")[0] ?? ""
7805
+ })
7806
+ };
7807
+ }
7808
+ if (!answer.ok) {
7809
+ return {
7810
+ ok: false,
7811
+ reason: answer.reason ?? t("plugin_install.host_fetch_failed", { target: source.target })
7812
+ };
7813
+ }
7814
+ if (!existsSync(answer.archivePath)) {
7815
+ return {
7816
+ ok: false,
7817
+ reason: t("plugin_install.host_fetch_no_file", { path: answer.archivePath })
7818
+ };
7819
+ }
7820
+ return extractArchive(answer.archivePath, staging);
7821
+ });
7639
7822
  }
7640
- function resolveEntrySource(entry, marketSource) {
7641
- const spec = entry.trim();
7642
- if (!spec.startsWith(".") && !spec.startsWith("/")) return spec;
7643
- const rel = spec.replace(/^\.\//, "").replace(/^\//, "");
7644
- if (rel === "" || rel.split("/").includes("..")) return void 0;
7645
- const market = marketSource.trim();
7646
- if (market.startsWith("github:") || market.startsWith("git+")) {
7647
- const [base = "", fragment] = market.split("#", 2);
7648
- const prefix = fragment?.startsWith("subdir=") ? `${fragment.slice("subdir=".length)}/` : "";
7649
- return `${base}#subdir=${prefix}${rel}`;
7823
+ function fetchLocal(source) {
7824
+ if (!existsSync(source.target)) {
7825
+ return { ok: false, reason: t("plugin_install.local_missing", { path: source.target }) };
7650
7826
  }
7651
- if (market.startsWith("https://") || market.startsWith("http://")) {
7652
- return `${market.replace(/\/[^/]*$/, "")}/${rel}`;
7827
+ return { ok: true };
7828
+ }
7829
+ async function fetchGit(source, staging, opts) {
7830
+ opts.onProgress?.(t("plugin_install.cloning", { target: source.target }));
7831
+ const result = await runCommand("git", ["clone", "--depth", "1", source.target, staging]);
7832
+ if (!result.ok) {
7833
+ return { ok: false, reason: t("plugin_install.clone_failed", { detail: result.detail }) };
7653
7834
  }
7654
- return join(marketDir(market), rel);
7835
+ rmSync(join(staging, ".git"), { recursive: true, force: true });
7836
+ return { ok: true };
7655
7837
  }
7656
- async function fetchCatalog(source) {
7657
- const input = source.trim();
7658
- if (!input) return { ok: false, reason: t("plugin_marketplace.source_empty") };
7659
- const local = localCatalogPath(input);
7660
- if (local) {
7661
- if (!existsSync(local)) {
7662
- return { ok: false, reason: t("plugin_marketplace.local_missing", { path: local }) };
7838
+ async function fetchNpm(source, staging, opts) {
7839
+ opts.onProgress?.(t("plugin_install.npm_fetching", { target: source.target }));
7840
+ mkdirSync(staging, { recursive: true });
7841
+ return withDownloadDir(async (dl) => {
7842
+ const packed = await runCommand(
7843
+ "npm",
7844
+ ["pack", source.target, "--pack-destination", dl, "--silent"],
7845
+ { cwd: dl, windowsShell: true }
7846
+ );
7847
+ if (!packed.ok) {
7848
+ return { ok: false, reason: t("plugin_install.npm_pack_failed", { detail: packed.detail }) };
7663
7849
  }
7664
- try {
7665
- return parseCatalog(readFileSync(local, "utf-8"), local);
7666
- } catch (err) {
7667
- return { ok: false, reason: t("plugin_marketplace.read_failed", { reason: first(err) }) };
7850
+ const tarball = packed.stdout.trim().split("\n").pop()?.trim();
7851
+ if (!tarball) return { ok: false, reason: t("plugin_install.npm_no_tarball") };
7852
+ return extractArchive(isAbsolute(tarball) ? tarball : join(dl, tarball), staging);
7853
+ });
7854
+ }
7855
+ async function fetchArchive(source, staging, opts) {
7856
+ opts.onProgress?.(t("plugin_install.downloading", { target: source.target }));
7857
+ mkdirSync(staging, { recursive: true });
7858
+ let bytes;
7859
+ try {
7860
+ const response = await fetch(source.target, { redirect: "follow" });
7861
+ if (!response.ok) {
7862
+ return {
7863
+ ok: false,
7864
+ reason: t("plugin_install.download_http", {
7865
+ status: response.status,
7866
+ statusText: response.statusText
7867
+ })
7868
+ };
7668
7869
  }
7669
- }
7670
- const url = catalogUrl(input);
7671
- if (!url) {
7870
+ bytes = Buffer.from(await response.arrayBuffer());
7871
+ } catch (err) {
7672
7872
  return {
7673
7873
  ok: false,
7674
- reason: t("plugin_marketplace.bad_url", { input, file: MARKETPLACE_FILE })
7874
+ reason: t("plugin_install.download_failed", {
7875
+ message: err instanceof Error ? err.message : String(err)
7876
+ })
7675
7877
  };
7676
7878
  }
7677
- try {
7678
- const response = await fetch(url, { redirect: "follow" });
7679
- if (!response.ok) {
7680
- const vars = { url, status: response.status };
7879
+ if (source.sha256) {
7880
+ const actual = createHash("sha256").update(bytes).digest("hex");
7881
+ if (actual !== source.sha256) {
7681
7882
  return {
7682
7883
  ok: false,
7683
- reason: looksPrivate(response.status) ? t("marketplace.fetch_failed_private", vars) : t("marketplace.fetch_failed", vars)
7884
+ reason: [
7885
+ t("plugin_install.sha_mismatch"),
7886
+ t("plugin_install.sha_expected", { expected: source.sha256 }),
7887
+ t("plugin_install.sha_actual", { actual })
7888
+ ].join("\n")
7684
7889
  };
7685
7890
  }
7686
- return parseCatalog(await response.text(), url);
7687
- } catch (err) {
7688
- return { ok: false, reason: t("marketplace.fetch_error", { url, reason: first(err) }) };
7689
7891
  }
7892
+ return withDownloadDir(async (dl) => {
7893
+ const archive = join(dl, `download${archiveExt(source.target)}`);
7894
+ writeFileSync(archive, bytes);
7895
+ return extractArchive(archive, staging);
7896
+ });
7690
7897
  }
7691
- function looksPrivate(status) {
7692
- return status === 401 || status === 403 || status === 404;
7898
+ function archiveExt(url) {
7899
+ const path = (url.split("?")[0] ?? "").toLowerCase();
7900
+ if (path.endsWith(".tgz")) return ".tgz";
7901
+ if (path.endsWith(".tar.gz")) return ".tar.gz";
7902
+ return ".zip";
7693
7903
  }
7694
- function catalogUrl(input) {
7695
- if (input.startsWith("github:")) {
7696
- const slug = input.slice("github:".length);
7697
- if (!/^[\w.-]+\/[\w.-]+$/.test(slug)) return void 0;
7698
- return `https://raw.githubusercontent.com/${slug}/HEAD/${MARKETPLACE_FILE}`;
7904
+ async function withDownloadDir(fn) {
7905
+ const dir = mkdtempSync(join(tmpdir(), "epoch-plugin-dl-"));
7906
+ try {
7907
+ return await fn(dir);
7908
+ } finally {
7909
+ rmSync(dir, { recursive: true, force: true });
7699
7910
  }
7700
- if (!input.startsWith("https://") && !input.startsWith("http://")) return void 0;
7701
- return input.endsWith(".json") ? input : `${input.replace(/\/+$/, "")}/${MARKETPLACE_FILE}`;
7702
7911
  }
7703
- function localCatalogPath(input) {
7704
- if (/^[a-z][a-z0-9+.-]+:/i.test(input) || input.startsWith("//")) return void 0;
7705
- const abs = isAbsolute(input) ? input : resolve(input);
7706
- if (existsSync(abs) && statSync(abs).isDirectory()) return join(abs, MARKETPLACE_FILE);
7707
- return abs;
7912
+ // src/plugin/root-token.ts
7913
+ var PLUGIN_ROOT_TOKEN = "${EPOCH_PLUGIN_ROOT}";
7914
+ var PLUGIN_ROOT_ENV = "EPOCH_PLUGIN_ROOT";
7915
+ function mentionsPluginRoot(text) {
7916
+ return text.includes(PLUGIN_ROOT_TOKEN);
7708
7917
  }
7709
- function marketDir(market) {
7710
- const abs = isAbsolute(market) ? market : resolve(market);
7711
- if (existsSync(abs) && statSync(abs).isDirectory()) return abs;
7712
- return abs.replace(/[/\\][^/\\]*$/, "");
7918
+ function substitutePluginRoot(text, root) {
7919
+ return text.split(PLUGIN_ROOT_TOKEN).join(root);
7713
7920
  }
7714
- function parseCatalog(body2, where) {
7715
- let raw;
7716
- try {
7717
- raw = JSON.parse(body2);
7718
- } catch (err) {
7921
+ async function uninstallPlugin(name, opts) {
7922
+ const removed = await new PluginStore(opts.statePath).mutate((records) => {
7923
+ const hit = records.find((r) => r.name === name);
7924
+ if (!hit) return void 0;
7925
+ return { records: records.filter((r) => r.name !== name), result: hit };
7926
+ });
7927
+ if (!removed) return { ok: false, reason: t("plugin_lifecycle.not_installed", { name }) };
7928
+ if (removed.linked) {
7929
+ rmSync(removed.path, { force: true });
7930
+ return { ok: true, message: t("plugin_lifecycle.uninstalled_link", { name }) };
7931
+ }
7932
+ rmSync(removed.path, { recursive: true, force: true });
7933
+ return { ok: true, message: t("plugin_lifecycle.uninstalled", { name }) };
7934
+ }
7935
+ async function setPluginEnabled(name, enabled, opts) {
7936
+ const changed = await new PluginStore(opts.statePath).mutate((records) => {
7937
+ const hit = records.find((r) => r.name === name);
7938
+ if (!hit) return void 0;
7719
7939
  return {
7720
- ok: false,
7721
- reason: t("plugin_marketplace.catalog_bad_json", { where, reason: first(err) })
7940
+ records: records.map((r) => r.name === name ? { ...r, enabled } : r),
7941
+ result: hit.enabled !== enabled
7722
7942
  };
7943
+ });
7944
+ if (changed === void 0) {
7945
+ return { ok: false, reason: t("plugin_lifecycle.not_installed", { name }) };
7723
7946
  }
7724
- const checked = parseLenient(CatalogSchema.optional(), raw, void 0);
7725
- if (!checked.value) {
7726
- const detail = checked.issues.map((i) => t("plugin_marketplace.issue", { path: i.path, message: i.message })).join(t("plugin_marketplace.catalog_issue_sep"));
7727
- return { ok: false, reason: t("plugin_marketplace.catalog_invalid", { where, detail }) };
7728
- }
7729
- return { ok: true, catalog: checked.value };
7947
+ const verb = enabled ? t("plugin_lifecycle.verb_enable") : t("plugin_lifecycle.verb_disable");
7948
+ return {
7949
+ ok: true,
7950
+ message: changed ? t("plugin_lifecycle.toggled", { verb, name }) : t("plugin_lifecycle.already", { name, verb })
7951
+ };
7730
7952
  }
7731
- function first(err) {
7732
- const message = err instanceof Error ? err.message : String(err);
7733
- return message.split("\n")[0] ?? t("plugin_marketplace.unknown_error");
7953
+ async function updatePlugin(name, opts) {
7954
+ const existing = new PluginStore(opts.statePath).read().records.find((r) => r.name === name);
7955
+ if (!existing) return { ok: false, reason: t("plugin_lifecycle.not_installed", { name }) };
7956
+ if (existing.linked) {
7957
+ return {
7958
+ ok: true,
7959
+ message: t("plugin_lifecycle.linked_no_update", { name, source: existing.source })
7960
+ };
7961
+ }
7962
+ return installPlugin(existing.source, {
7963
+ ...opts,
7964
+ replace: name,
7965
+ ...existing.marketplace ? { marketplace: existing.marketplace } : {}
7966
+ });
7734
7967
  }
7735
7968
  var TMP_SUFFIX = ".epoch-rewind";
7736
7969
  function planFor(entry, current) {
@@ -7778,7 +8011,7 @@ async function applyRewind(store, sessionId, manifest, apply, skippedConflicts)
7778
8011
  } catch (err) {
7779
8012
  return { ...empty, failed: t("rewind.stage_failed", { message: messageOf(err) }) };
7780
8013
  }
7781
- return commitAll(staged, skippedConflicts);
8014
+ return commitAll2(staged, skippedConflicts);
7782
8015
  }
7783
8016
  async function stageAll(store, sessionId, manifest, todo, byPath) {
7784
8017
  const staged = [];
@@ -7806,7 +8039,7 @@ async function stageAll(store, sessionId, manifest, todo, byPath) {
7806
8039
  }
7807
8040
  return staged;
7808
8041
  }
7809
- async function commitAll(staged, skippedConflicts) {
8042
+ async function commitAll2(staged, skippedConflicts) {
7810
8043
  const outcome = {
7811
8044
  restored: [],
7812
8045
  deleted: [],
@@ -8265,7 +8498,8 @@ function asStringList(value) {
8265
8498
  return value.filter((v) => typeof v === "string" && v.length > 0);
8266
8499
  }
8267
8500
  function toAbsolute(path, workDir) {
8268
- return isAbsolute(path) ? resolve(path) : resolve(workDir, path);
8501
+ const expanded = expandPath(path);
8502
+ return isAbsolute(expanded) ? resolve(expanded) : resolve(workDir, expanded);
8269
8503
  }
8270
8504
  var MAX_FILE_BYTES2 = 128 * 1024;
8271
8505
  var MAX_CALL_BYTES = 512 * 1024;
@@ -8676,8 +8910,8 @@ function sanitizeToolOutput(text) {
8676
8910
  if (!stripped.includes("\r")) return stripped;
8677
8911
  return stripped.split("\n").map((line) => line.includes("\r") ? line.slice(line.lastIndexOf("\r") + 1) : line).join("\n");
8678
8912
  }
8679
- var sleep2 = (ms) => new Promise((resolve16) => {
8680
- setTimeout(resolve16, ms);
8913
+ var sleep2 = (ms) => new Promise((resolve17) => {
8914
+ setTimeout(resolve17, ms);
8681
8915
  });
8682
8916
  async function* pumpToolOutput(target, start, options = {}) {
8683
8917
  const flushMs = options.flushMs ?? FLUSH_MS;
@@ -8745,8 +8979,8 @@ async function* pumpToolOutput(target, start, options = {}) {
8745
8979
  continue;
8746
8980
  }
8747
8981
  if (!running) break;
8748
- await new Promise((resolve16) => {
8749
- wake = resolve16;
8982
+ await new Promise((resolve17) => {
8983
+ wake = resolve17;
8750
8984
  });
8751
8985
  if (running && flushMs > 0) await sleep2(flushMs);
8752
8986
  }
@@ -8762,10 +8996,11 @@ var REPLY_LANG_RULE = {
8762
8996
  var REPLY_LANG_MATCH_USER = "- \u7528\u7528\u6237\u8DDF\u4F60\u8BF4\u8BDD\u7684\u90A3\u79CD\u8BED\u8A00\u56DE\u590D\u4ED6\uFF1A\u6B63\u6587\u548C\u601D\u8003\u90FD\u7528\u90A3\u79CD\u8BED\u8A00\u3002\u4EE3\u7801\u5E93\u3001\u5DE5\u5177\u8F93\u51FA\u3001\u62A5\u9519\u662F\u4EC0\u4E48\u8BED\u8A00\uFF0C\u90FD\u4E0D\u6539\u53D8\u8FD9\u4E00\u6761";
8763
8997
  function identityLine(role) {
8764
8998
  if (!role) return "\u4F60\u662F\u4E00\u4E2A\u672C\u5730\u667A\u80FD\u4F53\uFF0C\u53EF\u4EE5\u6267\u884C\u7EC8\u7AEF\u547D\u4EE4\u548C\u64CD\u4F5C\u6587\u4EF6\u3002";
8999
+ const who = role.displayName ?? role.name;
8765
9000
  if (role.scope === "session") {
8766
- return `\u4F60\u662F\u4E00\u4E2A\u672C\u5730\u667A\u80FD\u4F53\uFF0C\u8FD9\u4E00\u6B21\u4EE5\u300C${role.name}\u300D\u7684\u8EAB\u4EFD\u5DE5\u4F5C\u3002`;
9001
+ return `\u4F60\u662F\u4E00\u4E2A\u672C\u5730\u667A\u80FD\u4F53\uFF0C\u8FD9\u4E00\u6B21\u4EE5\u300C${who}\u300D\u7684\u8EAB\u4EFD\u5DE5\u4F5C\u3002`;
8767
9002
  }
8768
- return `\u4F60\u662F\u300C${role.name}\u300D\u5B50\u667A\u80FD\u4F53\uFF0C\u7531\u4E3B\u667A\u80FD\u4F53\u6D3E\u6765\u5B8C\u6210\u4E00\u4E2A\u72EC\u7ACB\u5B50\u4EFB\u52A1\u3002`;
9003
+ return `\u4F60\u662F\u300C${who}\u300D\u5B50\u667A\u80FD\u4F53\uFF0C\u7531\u4E3B\u667A\u80FD\u4F53\u6D3E\u6765\u5B8C\u6210\u4E00\u4E2A\u72EC\u7ACB\u5B50\u4EFB\u52A1\u3002`;
8769
9004
  }
8770
9005
  function systemPromptSegments(params) {
8771
9006
  return {
@@ -11019,6 +11254,37 @@ function pruneArtifacts(root, keepSession, retention = ARTIFACT_RETENTION_DEFAUL
11019
11254
  async function removeSessionArtifacts(sessionId, homeDir) {
11020
11255
  await rm(artifactsDir(sessionId, homeDir), { recursive: true, force: true });
11021
11256
  }
11257
+ function grantedPathsFor(args, workDir, extraRoots = []) {
11258
+ const out = /* @__PURE__ */ new Set();
11259
+ const add = (raw) => {
11260
+ if (isInWorkspace(toAbsolute2(raw, workDir), workDir, extraRoots)) return;
11261
+ out.add(approvalRootFor(raw, workDir));
11262
+ };
11263
+ for (const p of affectedPaths(args, workDir)) add(p);
11264
+ const workdir = args["workdir"];
11265
+ if (typeof workdir === "string" && workdir) add(workdir);
11266
+ return [...out];
11267
+ }
11268
+ function approvalRootFor(target, workDir) {
11269
+ const abs = toAbsolute2(target, workDir);
11270
+ try {
11271
+ if (statSync(abs).isDirectory()) return abs;
11272
+ } catch {
11273
+ }
11274
+ return dirname(abs);
11275
+ }
11276
+ function approvalEntryFor(opTarget, workDir, grantedRoots) {
11277
+ const abs = toAbsolute2(opTarget, workDir);
11278
+ if (isInWorkspace(abs, workDir, [])) return { target: opTarget, scope: "exact" };
11279
+ for (const root of grantedRoots) {
11280
+ if (isInWorkspace(abs, workDir, [root])) return { target: root, scope: "prefix" };
11281
+ }
11282
+ return { target: opTarget, scope: "exact" };
11283
+ }
11284
+ function toAbsolute2(path, workDir) {
11285
+ const expanded = expandPath(path);
11286
+ return isAbsolute(expanded) ? resolve(expanded) : resolve(workDir, expanded);
11287
+ }
11022
11288
  var MAX_TOOL_OUTPUT = 3e4;
11023
11289
  var MAX_SUB_CALL_OUTPUT = CODE_MODE_LIMITS.maxValueChars;
11024
11290
  function budgetOf2(tool, depth, path = "success") {
@@ -11266,6 +11532,7 @@ var ToolExecutor = class {
11266
11532
  sessionId,
11267
11533
  workDir: this.deps.workDir(),
11268
11534
  ...extraRoots?.length ? { extraRoots } : {},
11535
+ ...gate.grantedPaths?.length ? { grantedPaths: gate.grantedPaths } : {},
11269
11536
  ...homeDir ? { homeDir } : {},
11270
11537
  ...this.deps.sandboxTerminal !== void 0 ? { sandboxTerminal: this.deps.sandboxTerminal } : {},
11271
11538
  permissionLevel: this.deps.permissionManager?.getLevel() || "auto",
@@ -11400,6 +11667,11 @@ var ToolExecutor = class {
11400
11667
  const pm = this.deps.permissionManager;
11401
11668
  if (!pm) return { allowed: true };
11402
11669
  const toolName = et.name;
11670
+ const granted = () => grantedPathsFor(args, this.deps.workDir(), pm.getExtraRoots?.() ?? []);
11671
+ const allow = () => {
11672
+ const paths = granted();
11673
+ return paths.length > 0 ? { allowed: true, grantedPaths: paths } : { allowed: true };
11674
+ };
11403
11675
  const opType = mapOperation(et);
11404
11676
  const opTarget = this.resolveTarget(et, args);
11405
11677
  const opDetail = (typeof args["command"] === "string" ? args["command"] : typeof args["path"] === "string" ? args["path"] : opTarget) || argsKey(args);
@@ -11410,7 +11682,7 @@ var ToolExecutor = class {
11410
11682
  target: opTarget,
11411
11683
  toolName
11412
11684
  });
11413
- if (decision.allowed) return { allowed: true };
11685
+ if (decision.allowed) return allow();
11414
11686
  if (!decision.requireApproval) {
11415
11687
  return { allowed: false, output: `\u64CD\u4F5C\u88AB\u62E6\u622A [\u6743\u9650]: ${decision.reason || "\u9700\u8981\u6388\u6743"}` };
11416
11688
  }
@@ -11449,9 +11721,10 @@ var ToolExecutor = class {
11449
11721
  };
11450
11722
  }
11451
11723
  if (outcome === "allow-session" || outcome === "allow-always") {
11452
- pm.recordApproval(toolName, opTarget, outcome);
11724
+ const entry = approvalEntryFor(opTarget, this.deps.workDir(), granted());
11725
+ pm.recordApproval(toolName, entry.target, outcome, entry.scope);
11453
11726
  }
11454
- return { allowed: true };
11727
+ return allow();
11455
11728
  }
11456
11729
  };
11457
11730
  // src/agent/loop.ts
@@ -11853,7 +12126,13 @@ var AgentLoop = class {
11853
12126
  const role = this.config.role?.();
11854
12127
  yield {
11855
12128
  type: "run-start",
11856
- ...role ? { role: { name: role.name, ...role.source ? { source: role.source } : {} } } : {},
12129
+ ...role ? {
12130
+ role: {
12131
+ name: role.name,
12132
+ ...role.displayName ? { displayName: role.displayName } : {},
12133
+ ...role.source ? { source: role.source } : {}
12134
+ }
12135
+ } : {},
11857
12136
  estimatedTokens: estimated
11858
12137
  };
11859
12138
  if (learned.length > 0) {
@@ -13371,6 +13650,10 @@ var DEFAULT_MODELS = {
13371
13650
  groq: "llama-3.3-70b-versatile",
13372
13651
  mistral: "ministral-8b-latest",
13373
13652
  moonshotai: "kimi-latest",
13653
+ zhipu: "glm-4.5-flash",
13654
+ dashscope: "qwen-flash",
13655
+ bytedance: "doubao-seed-2.0-mini",
13656
+ qianfan: "ernie-speed-128k",
13374
13657
  cohere: "command-r7b-12-2024",
13375
13658
  xai: "grok-vision-beta",
13376
13659
  perplexity: "sonar",
@@ -13783,6 +14066,10 @@ var PACKAGES = {
13783
14066
  groq: "@ai-sdk/groq",
13784
14067
  mistral: "@ai-sdk/mistral",
13785
14068
  moonshotai: "@ai-sdk/moonshotai",
14069
+ zhipu: "@ai-sdk/openai-compatible",
14070
+ dashscope: "@ai-sdk/alibaba",
14071
+ bytedance: "@ai-sdk/openai-compatible",
14072
+ qianfan: "@ai-sdk/openai-compatible",
13786
14073
  cohere: "@ai-sdk/cohere",
13787
14074
  xai: "@ai-sdk/xai",
13788
14075
  perplexity: "@ai-sdk/perplexity",
@@ -13792,6 +14079,17 @@ var PACKAGES = {
13792
14079
  ollama: "ollama-ai-provider-v2",
13793
14080
  "openai-compatible": "@ai-sdk/openai-compatible"
13794
14081
  };
14082
+ function openaiCompatibleFactory(name, defaultBaseUrl) {
14083
+ return async ({ apiKey, baseUrl }) => {
14084
+ const { createOpenAICompatible } = await import('@ai-sdk/openai-compatible');
14085
+ return createOpenAICompatible({
14086
+ apiKey,
14087
+ baseURL: baseUrl || defaultBaseUrl,
14088
+ name,
14089
+ includeUsage: true
14090
+ });
14091
+ };
14092
+ }
13795
14093
  var LOADERS = {
13796
14094
  openai: async ({ apiKey, baseUrl }) => {
13797
14095
  const { createOpenAI } = await import('@ai-sdk/openai');
@@ -13823,6 +14121,17 @@ var LOADERS = {
13823
14121
  const { createMoonshotAI } = await import('@ai-sdk/moonshotai');
13824
14122
  return createMoonshotAI({ apiKey });
13825
14123
  },
14124
+ zhipu: openaiCompatibleFactory("zhipu", "https://open.bigmodel.cn/api/paas/v4"),
14125
+ dashscope: async ({ apiKey, baseUrl }) => {
14126
+ const { createAlibaba } = await import('@ai-sdk/alibaba');
14127
+ const provider = createAlibaba({
14128
+ apiKey,
14129
+ baseURL: baseUrl || "https://dashscope.aliyuncs.com/compatible-mode/v1"
14130
+ });
14131
+ return (model) => provider.languageModel(model);
14132
+ },
14133
+ bytedance: openaiCompatibleFactory("bytedance", "https://ark.cn-beijing.volces.com/api/v3"),
14134
+ qianfan: openaiCompatibleFactory("qianfan", "https://qianfan.baidubce.com/v2"),
13826
14135
  cohere: async ({ apiKey }) => {
13827
14136
  const { createCohere } = await import('@ai-sdk/cohere');
13828
14137
  return createCohere({ apiKey });
@@ -13851,15 +14160,7 @@ var LOADERS = {
13851
14160
  const { createOllama } = await import('ollama-ai-provider-v2');
13852
14161
  return createOllama();
13853
14162
  },
13854
- "openai-compatible": async ({ apiKey, baseUrl }) => {
13855
- const { createOpenAICompatible } = await import('@ai-sdk/openai-compatible');
13856
- return createOpenAICompatible({
13857
- apiKey,
13858
- baseURL: baseUrl || "http://localhost:8080/v1",
13859
- name: "custom",
13860
- includeUsage: true
13861
- });
13862
- }
14163
+ "openai-compatible": openaiCompatibleFactory("custom", "http://localhost:8080/v1")
13863
14164
  };
13864
14165
  var ProviderSdkMissingError = class extends Error {
13865
14166
  constructor(providerType, packageName, cause) {
@@ -14491,6 +14792,10 @@ var STATIC_MODEL_CATALOG = {
14491
14792
  ],
14492
14793
  mistral: ["mistral-large-latest", "mistral-small-latest", "codestral-latest"],
14493
14794
  moonshotai: ["kimi-latest"],
14795
+ zhipu: ["glm-4.6", "glm-4.5-flash", "glm-4.5-air", "glm-4.5"],
14796
+ dashscope: ["qwen3-max", "qwen-plus", "qwen-flash", "qwen-turbo", "qwen-max-latest"],
14797
+ bytedance: ["doubao-seed-2.1-pro", "doubao-seed-2.0-mini", "doubao-seed-1.6-flash"],
14798
+ qianfan: ["ernie-5.0", "ernie-speed-128k", "ernie-4.0-turbo-8k"],
14494
14799
  cohere: ["command-r-plus-04-2024", "command-r7b-12-2024", "command-a-03-2025"],
14495
14800
  xai: ["grok-vision-beta"],
14496
14801
  perplexity: ["sonar", "sonar-pro", "sonar-reasoning"],
@@ -14506,6 +14811,10 @@ var PROVIDER_DISCOVERY = {
14506
14811
  groq: { baseUrl: "https://api.groq.com/openai/v1", auth: "bearer" },
14507
14812
  mistral: { baseUrl: "https://api.mistral.ai/v1", auth: "bearer" },
14508
14813
  moonshotai: { baseUrl: "https://api.moonshot.ai/v1", auth: "bearer" },
14814
+ zhipu: { baseUrl: "https://open.bigmodel.cn/api/paas/v4", auth: "bearer" },
14815
+ dashscope: { baseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1", auth: "bearer" },
14816
+ bytedance: { baseUrl: "https://ark.cn-beijing.volces.com/api/v3", auth: "bearer" },
14817
+ qianfan: { baseUrl: "https://qianfan.baidubce.com/v2", auth: "bearer" },
14509
14818
  xai: { baseUrl: "https://api.x.ai/v1", auth: "bearer" },
14510
14819
  perplexity: { baseUrl: "https://api.perplexity.ai", auth: "bearer" },
14511
14820
  togetherai: { baseUrl: "https://api.together.xyz/v1", auth: "bearer" }
@@ -14756,7 +15065,7 @@ function writeUserYaml(path, key, value) {
14756
15065
  const after = asMapping(parseYaml(next));
14757
15066
  const intact = after !== null && (dot < 0 ? onlyChanged(before, after, key, value) : onlyChangedNested(before, after, key.slice(0, dot), key.slice(dot + 1), value));
14758
15067
  if (!intact) return { ok: false, reason: "unwritable-file" };
14759
- return commit2(path, next, 384, value);
15068
+ return commit(path, next, 384, value);
14760
15069
  }
14761
15070
  function writeSettingsJson(path, key, value) {
14762
15071
  let before = {};
@@ -14776,7 +15085,7 @@ function writeSettingsJson(path, key, value) {
14776
15085
  }
14777
15086
  const body2 = `${JSON.stringify({ ...before, [key]: value }, null, 2)}
14778
15087
  `;
14779
- return commit2(path, body2, 420, value);
15088
+ return commit(path, body2, 420, value);
14780
15089
  }
14781
15090
  function scalarText(value) {
14782
15091
  return dump(value, { lineWidth: -1 }).trimEnd();
@@ -14833,7 +15142,7 @@ function sameKeysAndValues(before, after, except) {
14833
15142
  function sameData(a, b) {
14834
15143
  return JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
14835
15144
  }
14836
- function commit2(path, body2, createMode, value) {
15145
+ function commit(path, body2, createMode, value) {
14837
15146
  const tmp = `${path}.${process.pid}.tmp`;
14838
15147
  try {
14839
15148
  mkdirSync(dirname(path), { recursive: true });
@@ -18324,7 +18633,17 @@ function checkCodeExec(isolation) {
18324
18633
  function checkAuto() {
18325
18634
  return { allowed: true, code: "level-default" };
18326
18635
  }
18327
- function checkPlan(operation) {
18636
+ function outsideWorkspace(operation, extraRoots) {
18637
+ if (operation.type !== "file_read" && operation.type !== "file_write") return null;
18638
+ if (isInWorkspace(operation.target, operation.workDir, extraRoots)) return null;
18639
+ return {
18640
+ allowed: false,
18641
+ requireApproval: true,
18642
+ reason: t("permission_reason.outside_workspace"),
18643
+ code: "level-default"
18644
+ };
18645
+ }
18646
+ function checkPlan(operation, extraRoots) {
18328
18647
  if (operation.type === "code_exec") {
18329
18648
  return {
18330
18649
  allowed: false,
@@ -18342,25 +18661,16 @@ function checkPlan(operation) {
18342
18661
  code: "level-default"
18343
18662
  };
18344
18663
  }
18345
- return { allowed: true, code: "level-default" };
18664
+ return outsideWorkspace(operation, extraRoots) ?? { allowed: true, code: "level-default" };
18346
18665
  }
18347
18666
  function checkAcceptEdits(operation, isolation, extraRoots) {
18348
18667
  if (operation.type === "code_exec") return checkCodeExec(isolation);
18349
- if (operation.type === "file_read" || operation.type === "file_write") {
18350
- if (isInWorkspace(operation.target, operation.workDir, extraRoots)) {
18351
- return { allowed: true, code: "level-default" };
18352
- }
18353
- return {
18354
- allowed: false,
18355
- requireApproval: true,
18356
- reason: t("permission_reason.outside_workspace"),
18357
- code: "level-default"
18358
- };
18359
- }
18360
- return { allowed: true, code: "level-default" };
18668
+ return outsideWorkspace(operation, extraRoots) ?? { allowed: true, code: "level-default" };
18361
18669
  }
18362
- function checkDefault(operation) {
18363
- if (operation.type === "file_read") return { allowed: true, code: "level-default" };
18670
+ function checkDefault(operation, extraRoots) {
18671
+ if (operation.type === "file_read") {
18672
+ return outsideWorkspace(operation, extraRoots) ?? { allowed: true, code: "level-default" };
18673
+ }
18364
18674
  if (operation.type === "command" && isReadOnlyCommand(operation.detail)) {
18365
18675
  return { allowed: true, code: "level-default" };
18366
18676
  }
@@ -18381,12 +18691,12 @@ function decideByLevel(level, operation, isolation, extraRoots = []) {
18381
18691
  case "auto":
18382
18692
  return checkAuto();
18383
18693
  case "plan":
18384
- return checkPlan(operation);
18694
+ return checkPlan(operation, extraRoots);
18385
18695
  case "acceptEdits":
18386
18696
  return checkAcceptEdits(operation, isolation, extraRoots);
18387
18697
  case "default":
18388
18698
  default:
18389
- return checkDefault(operation);
18699
+ return checkDefault(operation, extraRoots);
18390
18700
  }
18391
18701
  }
18392
18702
  var SENSITIVE_PATHS = [
@@ -19965,7 +20275,7 @@ function parseSimpleToml(content, file) {
19965
20275
  const at = `${file}:${i + 1}`;
19966
20276
  if (!trimmed || trimmed.startsWith("#")) continue;
19967
20277
  if (trimmed === "[[rule]]") {
19968
- if (current) commit3(current, file, rules, issues);
20278
+ if (current) commit2(current, file, rules, issues);
19969
20279
  current = { raw: {}, line: i + 1 };
19970
20280
  continue;
19971
20281
  }
@@ -19988,7 +20298,7 @@ function parseSimpleToml(content, file) {
19988
20298
  current.raw[kv.key] = kv.value;
19989
20299
  }
19990
20300
  }
19991
- if (current) commit3(current, file, rules, issues);
20301
+ if (current) commit2(current, file, rules, issues);
19992
20302
  return { rules, issues };
19993
20303
  }
19994
20304
  function readKeyValue(line) {
@@ -20002,7 +20312,7 @@ function readKeyValue(line) {
20002
20312
  const sq = rawVal.match(/^'(.*)'$/);
20003
20313
  return { key, value: sq && sq[1] ? sq[1] : rawVal };
20004
20314
  }
20005
- function commit3(draft, file, rules, issues) {
20315
+ function commit2(draft, file, rules, issues) {
20006
20316
  const at = `${file}:${draft.line}`;
20007
20317
  issues.push(...unknownKeyIssues(POLICY_RULE_KEYS, draft.raw, at));
20008
20318
  const raw = { ...draft.raw };
@@ -20913,14 +21223,14 @@ function runCommandHook(config, event, workDir) {
20913
21223
  const baseEnv = sanitizeEnvironment(process.env);
20914
21224
  const injectedEnv = injectEpochEnv(event, config.pluginRoot);
20915
21225
  const hookEnv = { ...baseEnv, ...injectedEnv, ...config.env };
20916
- return new Promise((resolve16) => {
21226
+ return new Promise((resolve17) => {
20917
21227
  let stdout = "";
20918
21228
  let stderr = "";
20919
21229
  let settled = false;
20920
21230
  const failToStart = (err) => {
20921
21231
  if (settled) return;
20922
21232
  settled = true;
20923
- resolve16({
21233
+ resolve17({
20924
21234
  exitCode: -1,
20925
21235
  stdout,
20926
21236
  stderr,
@@ -20942,7 +21252,7 @@ function runCommandHook(config, event, workDir) {
20942
21252
  if (!settled) {
20943
21253
  settled = true;
20944
21254
  child.kill("SIGKILL");
20945
- resolve16({
21255
+ resolve17({
20946
21256
  exitCode: -1,
20947
21257
  stdout,
20948
21258
  stderr,
@@ -20978,7 +21288,7 @@ function runCommandHook(config, event, workDir) {
20978
21288
  }
20979
21289
  } catch {
20980
21290
  }
20981
- resolve16({
21291
+ resolve17({
20982
21292
  exitCode,
20983
21293
  stdout: stdout.trim(),
20984
21294
  stderr: stderr.trim(),
@@ -22490,7 +22800,7 @@ async function importSkills(source, opts) {
22490
22800
  detail: t("skill.import_stale", void 0, opts.lang)
22491
22801
  };
22492
22802
  }
22493
- return { ok: true, ...commit4(preview3, staged, opts.skillsDir, opts.lang) };
22803
+ return { ok: true, ...commit3(preview3, staged, opts.skillsDir, opts.lang) };
22494
22804
  });
22495
22805
  }
22496
22806
  async function withStaged(source, opts, fn) {
@@ -22609,7 +22919,7 @@ function digest(entries2, staged) {
22609
22919
  }
22610
22920
  return hash.digest("hex").slice(0, 32);
22611
22921
  }
22612
- function commit4(preview3, staged, skillsDir, lang) {
22922
+ function commit3(preview3, staged, skillsDir, lang) {
22613
22923
  const byName = new Map(staged.map((s) => [s.name, s.dir]));
22614
22924
  const imported = [];
22615
22925
  const skipped = [];
@@ -23761,7 +24071,7 @@ var CodeSandbox = class {
23761
24071
  stdio: ["ignore", "pipe", "pipe"],
23762
24072
  ...IS_WINDOWS ? { windowsHide: true } : {}
23763
24073
  };
23764
- return new Promise((resolve16) => {
24074
+ return new Promise((resolve17) => {
23765
24075
  let stdout = "";
23766
24076
  let stderr = "";
23767
24077
  let killedForOutput = false;
@@ -23772,7 +24082,7 @@ var CodeSandbox = class {
23772
24082
  const failToStart = (err) => {
23773
24083
  opts.signal?.removeEventListener("abort", onAbort);
23774
24084
  if (err.code === "ENOENT") {
23775
- resolve16({
24085
+ resolve17({
23776
24086
  success: false,
23777
24087
  output: "",
23778
24088
  error: `[\u9519\u8BEF] \u627E\u4E0D\u5230\u89E3\u91CA\u5668: ${command}`,
@@ -23781,7 +24091,7 @@ var CodeSandbox = class {
23781
24091
  });
23782
24092
  return;
23783
24093
  }
23784
- resolve16({
24094
+ resolve17({
23785
24095
  success: false,
23786
24096
  output: "",
23787
24097
  error: `[\u9519\u8BEF] ${err.message}\uFF08command=${command}\uFF0Ccwd=${spawnOpts.cwd ?? ""}\uFF09`,
@@ -23817,7 +24127,7 @@ var CodeSandbox = class {
23817
24127
  if (stderr.length < MAX_OUTPUT) stderr += errDecoder?.end() ?? "";
23818
24128
  const duration = Date.now() - start;
23819
24129
  if (killedForOutput) {
23820
- resolve16({
24130
+ resolve17({
23821
24131
  success: false,
23822
24132
  output: trunc(stdout, stderr),
23823
24133
  error: `[\u8F93\u51FA\u8D85\u9650] \u8D85\u8FC7 ${MAX_OUTPUT} \u5B57\u8282\uFF0C\u5DF2\u7EC8\u6B62`,
@@ -23827,7 +24137,7 @@ var CodeSandbox = class {
23827
24137
  return;
23828
24138
  }
23829
24139
  if (sig) {
23830
- resolve16({
24140
+ resolve17({
23831
24141
  success: false,
23832
24142
  output: trunc(stdout, stderr),
23833
24143
  error: opts.signal?.aborted ? "[\u5DF2\u53D6\u6D88]" : "[\u8D85\u65F6] \u6267\u884C\u8D85\u65F6",
@@ -23839,7 +24149,7 @@ var CodeSandbox = class {
23839
24149
  const output = trunc(stdout, stderr);
23840
24150
  if (code !== 0 && code !== null) {
23841
24151
  const oom = /heap out of memory|allocation failed/i.test(stderr);
23842
- resolve16({
24152
+ resolve17({
23843
24153
  success: false,
23844
24154
  output,
23845
24155
  error: oom ? `[\u5185\u5B58\u8D85\u9650] \u8D85\u8FC7 ${MAX_MEMORY}MB` : `[\u8FD0\u884C\u65F6\u9519\u8BEF] exit ${code}: ${stderr.slice(0, 200)}`,
@@ -23848,7 +24158,7 @@ var CodeSandbox = class {
23848
24158
  });
23849
24159
  return;
23850
24160
  }
23851
- resolve16({ success: true, output, duration });
24161
+ resolve17({ success: true, output, duration });
23852
24162
  });
23853
24163
  });
23854
24164
  }
@@ -23908,4 +24218,4 @@ function trunc(stdout, stderr) {
23908
24218
  * `--tree` 的命令帮助里,让用户自己知道 `--tree` 意味着什么。
23909
24219
  */
23910
24220
 
23911
- export { AGENT_ROLE_DIAG_MODULE, ARTIFACT_MAX_TOTAL_BYTES, ARTIFACT_RETENTION_DEFAULTS, ATTACHMENT_MAX_TOTAL_BYTES, ATTACHMENT_RETENTION_DEFAULTS, AgentLoop, AppearanceStore, ApprovalCache, BLOCKED_TOOLS, BudgetGuard, BudgetStore, CHECKPOINT_MAX_TOTAL_BYTES, CLAUDE_EVENT_NAMES, CLAUDE_TOOL_NAMES, CODE_MODE_LIMITS, CODE_MODE_SDK_TAG, CONFIG_KEYS, CURRENT_CONFIG_VERSION, CheckpointManager, CheckpointStore, CodeSandbox, ComplianceEngine, ContextCompressor, DEFAULT_COMPLIANCE_ACTIONS, DEFAULT_COMPLIANCE_SETTINGS, DEFAULT_COMPRESSION_THRESHOLD, DEFAULT_GOAL_MAX_ROUNDS, DEFAULT_MAX_HOLD_CHARS, DelegateManager, EMPTY_RULES, EPOCH_DISK_LEDGER, EpochConfigSchema, FTS_TABLES, FTS_TRIGGERS, GOAL_BLOCK_CODES, GoalService, GoalStore, HEADLESS_AUDIT_CODES, HookManager, INSTRUCTION_BUDGET_CHARS, INSTRUCTION_FILES, ImageLoadError, ImportTrustStore, LAUNCHD_LABEL_PREFIX, LOCK_HEARTBEAT_MS, LOCK_STALE_MS, LastOpenSessions, MARKETPLACE_DISPLAY_FIELDS, MARKETPLACE_FILE, MAX_AUDIT_ENTRIES, MAX_COMMAND_DEPTH, MAX_GOAL_MAX_ROUNDS, MAX_IMPORT_DEPTH, MAX_LAST_OPEN_SESSIONS, MAX_OBJECTIVE_CHARS, MAX_REASON_CHARS, MAX_RECENT_WORKSPACES, MODEL_GIVE_UP_THRESHOLD, MULTIMODAL_DEFAULTS, ManagedPolicyError, ManagedSettingsSchema, MemoryManager, ModelFailureTracker, NOMINAL_TOTAL_BYTES, NO_REVEALED_TOOLS, PACK_EXCLUDES, PACK_MAX_BYTES, PACK_MAX_FILES, PERMISSION_AUDIT_CODES, PLUGIN_LAYOUT, PLUGIN_MANIFEST_FILE, PLUGIN_MANIFEST_KEYS, PLUGIN_NAME_PATTERN, PLUGIN_ROOT_ENV, PLUGIN_ROOT_TOKEN, PLUGIN_VISIBILITY, POLICY_DECISIONS, PROBE_VERSION, PROJECT_AGENTS_SUBDIR, PROVIDER_NO_CREDENTIAL, PermissionManager, PlanModeState, PluginAuthorSchema, PluginStore, PolicyRuleSchema, ProviderError, ProviderRouter, RECORDING_KEEP_RUNS, RECORDING_MAX_BYTES, ROLE_FRONTMATTER_SCHEMA, RUN_CODE_TOOL, RecentWorkspaces, SANDBOX_COVERS, SANDBOX_EXCLUDES, SCALAR_KEYS, SCHEDULE_DEFAULTS, SCHEDULE_DEFAULT_ALLOWLIST, SCHEDULE_FORM_BUDGET_USD, SCHEDULE_ISSUE_CODES, SCHTASKS_FOLDER, SESSION_MESSAGES_READ_TOOL, SESSION_MESSAGES_SEARCH_TOOL, SESSION_QUERY_TOOLS, SESSION_SEARCH_TOOL, SESSION_TRACE_TOOL, SETTINGS_LAYER_ORDER, SETTING_WRITE_KEYS, SETTING_WRITE_LAYERS, SKILL_CREATE_TOOL, SKILL_EDIT_TOOL, SKILL_PATCH_TOOL, SKILL_VIEW_TOOL, SKILL_WRITE_FILE_TOOL, SLASH_COMMANDS_DIAG_MODULE, ScheduleRecorder, ScheduleRegistrar, ScheduleStore, SessionManager, SessionQueries, SessionReferences, SettingsFileError, SettingsFileSchema, SkillLearner, SkillSystem, TOOL_SEARCH_TOOL, TaskNoticeTracker, ToolRegistry, TrackerDB, TrustManager, Workspace, acquireScheduleLock, addMarketplace, addUsage, admitArtifact, admitArtifacts, affectedPaths, artifactLaunchArgv, artifactOpenRefusal, assertLevelAllowed, blockedRoleTools, buildContextBreakdown, buildFireArgv, buildFtsMatchQuery, buildSchemaExports, buildSystemPrompt, buildTrigramMatchQuery, builtinRoles, checkEpochVersion, checkFtsHealth, checkShadowing, classifyPersistFailure, classifyProviderError, clearModelCache, coalesceRecording, codeModeWorkerPath, commandNameFromRelPath, compileRuleLists, compressionOptions, computeCost, computeCostMicros, computeExecHash, countRules, createAllowAllGate, createAllowAllImportGate, createAskQuestionTool, createCodeExecTool, createDelegateTool, createDenyAllGate, createDenyAllImportGate, createGoalTool, createImportGate, createMemoryTool, createPlanModeTools, createRoleFile, createRoleScope, createRunCodeTool, createScheduleDraftTool, createSessionMessagesReadTool, createSessionMessagesSearchTool, createSessionSearchTool, createSessionSearchTools, createSessionTraceTool, createSkillViewTool, createSkillWriteTools, createTodoTool, createToolRevealState, createToolScope, createToolSearchTool, createTrustGate, currentEpochVersion, decideImport, decideTrust, defaultConfig, defaultModelOf, describeArtifactRefusal, describeBackgroundTask, describeHeadlessPolicy, describeIsolation, describeJsonError, diagnoseSchedules, discoverMarkdown, discoverModels, discoverProject, displayAllows, displayCurrency, draftCommandLine, draftWidening, escapeRuleContent, estimateImageTokens, estimateRunTokens, estimateTokens, evaluateSelection, expandCommand, expandImports, explainSetting, extractArchive, findInstructionFiles, findProjectInstructions, findRole, formatBytes, formatSkillIndex, formatUsd, gateForTool, getCapability, getConfigIssues, getConfigOrigins, getImportGate, getTrustGate, hasDedicatedExecContent, hasExecTierContent, hasPromptCaching, hasProviderCredentials, hasReadTierContent, imageFromBase64, imageFromPath, imagePartTokens, importParts, importSkills, installPlugin, instructionDirs, instructionNotices, intervalSlots, inventoryProjectExtensions, inventoryTotal, isBlockCode, isCodeModeDeferred, isEmptyRules, isGoalPhase, isNonInteractive, isReadOnlySubCall, isSettingWriteLayer, isTransientPersistFailure, isValidPluginName, isWithinWindow, isWritableSettingKey, labelSlug, layerRank, listRunningSchedules, listSessionCandidates, listWorkspaceFiles, loadCommandDefinitions, loadCommandFile, loadCommands, loadConfig, loadHookSources, loadKeybindings, loadLexiconDir, loadLexiconFiles, loadPlugins, loadPolicyFiles, loadPolicyRules, loadRoleDefinitions, loadRoleDir, loadedImports, localDateString, managedSwitchNotes, mapClaudeToolName, mapEpochToolName, mapOperationType, matchPreauthorization, matchRuleLists, matchesHook, mentionsPluginRoot, mergeHeadlessPolicy, mergeRoles, mergeSchedulePatch, microsToUsd, modelListCandidates, needsAllowlist, nextFallbackModel, nextRunAt, noConfigOrigins, noManagedPolicy, noPlugins, openArtifact, originOf, packFileName, packPlugin, parseClaudeSettingsHooks, parseClock, parseDate, parseFrontmatter, parseHooksConfig, parseHooksConfigVerbose, parseImportLine, parseModelList, parseRoleDefinition, parseScheduleDraft, parseSkillFrontmatter, parseTrigger, pendingImageTokens, persistFailureCode, persistParts, persistToolResults, persistedUsage, planPack, previewSkillImport, pricingFor, projectEntryForDisplay, pruneArtifacts, pruneAttachments, pruneRecordings, pruneSchedules, readImageSize, readMarketplaces, readPluginManifest, readPluginRecords, readProviderKeyEnv, readRecording, readSessionSurface, reclaimFtsSpace, redactLine, refreshOpenRouterMetadata, removeMarketplace, removeRecordings, removeRoleFile, removeSessionArtifacts, removeSessionAttachments, renderImportTree, renderProjectContext, renderSdkDeclaration, renderSelfIndex, repairSchedules, resolveArtifactRetention, resolveCodeModeWorker, resolveComplianceDirs, resolveEntrySource, resolveInWorkspace, resolveMarketplaceRef, resolveMentions, resolveMultimodalPolicy, resolvePolicyDirs, resolvePricingAt, resolveProjectRoot, resolveScheduleBackend, resolveSessionReference, resolveSessionVisibility, resolveSettings, resolveSkillDirs, resolveSource, resolveTar, resolveWorkspace, restoreParts, revealScopedProvider, roleScopedProvider, roleSourceLabel, ruleCovers, ruleFromString, ruleToString, runCode, runCommand2 as runCommand, sanitizeArgNames, sanitizeCommand, sanitizeFtsQuery, sanitizePath, sanitizeToolOutput, scanInstructions, scanPluginDir, scanSkillDirs, searchMarketplaces, setDisplayCurrency, setImportGate, setPluginEnabled, setTrustGate, settingValueChoices, settingValueKind, settingWriteApply, settingWriteLayers, settingWriteTargets, skillIndexDescription, skillIndexResidency, skillIndexStats, sniffMediaType, splitFrontmatter, staticToolProvider, stripArtifactData, substitutePluginRoot, suggestRuleFromApproval, systemOpenArgv, systemPromptSegments, toPosix, toolDefinitionTokens, toolMatches, toolTableTokens, totalTokens, traceSession, triggerPeriodMs, unescapeRuleContent, uninstallPlugin, unknownRoleSkills, unknownRoleTools, unloadedInstructionFiles, updateMarketplace, updatePlugin, usdToMicros, validateSchedule, validateTrigger, validateWindow, withGoalCompression, withGoalPrompt, workspaceDisplayPath, writeSettingValue };
24221
+ export { AGENT_ROLE_DIAG_MODULE, ARTIFACT_MAX_TOTAL_BYTES, ARTIFACT_RETENTION_DEFAULTS, ATTACHMENT_MAX_TOTAL_BYTES, ATTACHMENT_RETENTION_DEFAULTS, AgentLoop, AppearanceStore, ApprovalCache, BLOCKED_TOOLS, BudgetGuard, BudgetStore, CHECKPOINT_MAX_TOTAL_BYTES, CLAUDE_EVENT_NAMES, CLAUDE_TOOL_NAMES, CODE_MODE_LIMITS, CODE_MODE_SDK_TAG, CONFIG_KEYS, CURRENT_CONFIG_VERSION, CheckpointManager, CheckpointStore, CodeSandbox, ComplianceEngine, ContextCompressor, DEFAULT_COMPLIANCE_ACTIONS, DEFAULT_COMPLIANCE_SETTINGS, DEFAULT_COMPRESSION_THRESHOLD, DEFAULT_GOAL_MAX_ROUNDS, DEFAULT_MAX_HOLD_CHARS, DelegateManager, EMPTY_RULES, EPOCH_DISK_LEDGER, EpochConfigSchema, FTS_TABLES, FTS_TRIGGERS, GOAL_BLOCK_CODES, GoalService, GoalStore, HEADLESS_AUDIT_CODES, HookManager, INSTRUCTION_BUDGET_CHARS, INSTRUCTION_FILES, ImageLoadError, ImportTrustStore, LAUNCHD_LABEL_PREFIX, LOCK_HEARTBEAT_MS, LOCK_STALE_MS, LastOpenSessions, MARKETPLACE_DISPLAY_FIELDS, MARKETPLACE_FILE, MAX_AUDIT_ENTRIES, MAX_COMMAND_DEPTH, MAX_GOAL_MAX_ROUNDS, MAX_IMPORT_DEPTH, MAX_LAST_OPEN_SESSIONS, MAX_OBJECTIVE_CHARS, MAX_REASON_CHARS, MAX_RECENT_WORKSPACES, MODEL_GIVE_UP_THRESHOLD, MULTIMODAL_DEFAULTS, ManagedPolicyError, ManagedSettingsSchema, MemoryManager, ModelFailureTracker, NOMINAL_TOTAL_BYTES, NO_REVEALED_TOOLS, PACK_EXCLUDES, PACK_MAX_BYTES, PACK_MAX_FILES, PERMISSION_AUDIT_CODES, PLUGIN_LAYOUT, PLUGIN_MANIFEST_FILE, PLUGIN_MANIFEST_KEYS, PLUGIN_NAME_PATTERN, PLUGIN_ROOT_ENV, PLUGIN_ROOT_TOKEN, PLUGIN_VISIBILITY, POLICY_DECISIONS, PROBE_VERSION, PROJECT_AGENTS_SUBDIR, PROVIDER_NO_CREDENTIAL, PermissionManager, PlanModeState, PluginAuthorSchema, PluginStore, PolicyRuleSchema, ProviderError, ProviderRouter, RECORDING_KEEP_RUNS, RECORDING_MAX_BYTES, ROLE_FRONTMATTER_SCHEMA, RUN_CODE_TOOL, RecentWorkspaces, SANDBOX_COVERS, SANDBOX_EXCLUDES, SCALAR_KEYS, SCHEDULE_DEFAULTS, SCHEDULE_DEFAULT_ALLOWLIST, SCHEDULE_FORM_BUDGET_USD, SCHEDULE_ISSUE_CODES, SCHTASKS_FOLDER, SESSION_MESSAGES_READ_TOOL, SESSION_MESSAGES_SEARCH_TOOL, SESSION_QUERY_TOOLS, SESSION_SEARCH_TOOL, SESSION_TRACE_TOOL, SETTINGS_LAYER_ORDER, SETTING_WRITE_KEYS, SETTING_WRITE_LAYERS, SKILL_CREATE_TOOL, SKILL_EDIT_TOOL, SKILL_PATCH_TOOL, SKILL_VIEW_TOOL, SKILL_WRITE_FILE_TOOL, SLASH_COMMANDS_DIAG_MODULE, ScheduleRecorder, ScheduleRegistrar, ScheduleStore, SessionManager, SessionQueries, SessionReferences, SettingsFileError, SettingsFileSchema, SkillLearner, SkillSystem, TOOL_SEARCH_TOOL, TaskNoticeTracker, ToolRegistry, TrackerDB, TrustManager, Workspace, acquireScheduleLock, addMarketplace, addUsage, admitArtifact, admitArtifacts, affectedPaths, approvalEntryFor, approvalRootFor, artifactLaunchArgv, artifactOpenRefusal, assertLevelAllowed, blockedRoleTools, buildContextBreakdown, buildFireArgv, buildFtsMatchQuery, buildSchemaExports, buildSystemPrompt, buildTrigramMatchQuery, builtinRoles, checkEpochVersion, checkFtsHealth, checkShadowing, classifyPersistFailure, classifyProviderError, clearModelCache, coalesceRecording, codeModeWorkerPath, commandNameFromRelPath, compileRuleLists, compressionOptions, computeCost, computeCostMicros, computeExecHash, countRules, createAllowAllGate, createAllowAllImportGate, createAskQuestionTool, createCodeExecTool, createDelegateTool, createDenyAllGate, createDenyAllImportGate, createGoalTool, createImportGate, createMemoryTool, createPlanModeTools, createRoleFile, createRoleScope, createRunCodeTool, createScheduleDraftTool, createSessionMessagesReadTool, createSessionMessagesSearchTool, createSessionSearchTool, createSessionSearchTools, createSessionTraceTool, createSkillViewTool, createSkillWriteTools, createTodoTool, createToolRevealState, createToolScope, createToolSearchTool, createTrustGate, currentEpochVersion, decideImport, decideTrust, defaultConfig, defaultModelOf, describeArtifactRefusal, describeBackgroundTask, describeHeadlessPolicy, describeIsolation, describeJsonError, diagnoseSchedules, discoverMarkdown, discoverModels, discoverProject, displayAllows, displayCurrency, draftCommandLine, draftWidening, escapeRuleContent, estimateImageTokens, estimateRunTokens, estimateTokens, evaluateSelection, expandCommand, expandImports, explainSetting, extractArchive, findInstructionFiles, findProjectInstructions, findRole, formatBytes, formatSkillIndex, formatUsd, gateForTool, getCapability, getConfigIssues, getConfigOrigins, getImportGate, getTrustGate, grantedPathsFor, hasDedicatedExecContent, hasExecTierContent, hasPromptCaching, hasProviderCredentials, hasReadTierContent, imageFromBase64, imageFromPath, imagePartTokens, importParts, importSkills, installPlugin, instructionDirs, instructionNotices, intervalSlots, inventoryProjectExtensions, inventoryTotal, isBlockCode, isCodeModeDeferred, isEmptyRules, isGoalPhase, isNonInteractive, isReadOnlySubCall, isSettingWriteLayer, isTransientPersistFailure, isValidPluginName, isWithinWindow, isWritableSettingKey, labelSlug, layerRank, listRunningSchedules, listSessionCandidates, listWorkspaceFiles, loadCommandDefinitions, loadCommandFile, loadCommands, loadConfig, loadHookSources, loadKeybindings, loadLexiconDir, loadLexiconFiles, loadPlugins, loadPolicyFiles, loadPolicyRules, loadRoleDefinitions, loadRoleDir, loadedImports, localDateString, managedSwitchNotes, mapClaudeToolName, mapEpochToolName, mapOperationType, matchPreauthorization, matchRuleLists, matchesHook, mentionsPluginRoot, mergeHeadlessPolicy, mergeRoles, mergeSchedulePatch, microsToUsd, modelListCandidates, needsAllowlist, nextFallbackModel, nextRunAt, noConfigOrigins, noManagedPolicy, noPlugins, openArtifact, originOf, packFileName, packPlugin, parseClaudeSettingsHooks, parseClock, parseDate, parseFrontmatter, parseHooksConfig, parseHooksConfigVerbose, parseImportLine, parseModelList, parseRoleDefinition, parseScheduleDraft, parseSkillFrontmatter, parseTrigger, pendingImageTokens, persistFailureCode, persistParts, persistToolResults, persistedUsage, planPack, previewSkillImport, pricingFor, projectEntryForDisplay, pruneArtifacts, pruneAttachments, pruneRecordings, pruneSchedules, readImageSize, readMarketplaces, readPluginManifest, readPluginRecords, readProviderKeyEnv, readRecording, readSessionSurface, reclaimFtsSpace, redactLine, refreshOpenRouterMetadata, removeMarketplace, removeRecordings, removeRoleFile, removeSessionArtifacts, removeSessionAttachments, renderImportTree, renderProjectContext, renderSdkDeclaration, renderSelfIndex, repairSchedules, resolveArtifactRetention, resolveCodeModeWorker, resolveComplianceDirs, resolveEntrySource, resolveInWorkspace, resolveMarketplaceRef, resolveMentions, resolveMultimodalPolicy, resolvePolicyDirs, resolvePricingAt, resolveProjectRoot, resolveScheduleBackend, resolveSessionReference, resolveSessionVisibility, resolveSettings, resolveSkillDirs, resolveSource, resolveTar, resolveWorkspace, restoreParts, revealScopedProvider, roleScopedProvider, roleSourceLabel, ruleCovers, ruleFromString, ruleToString, runCode, runCommand2 as runCommand, sanitizeArgNames, sanitizeCommand, sanitizeFtsQuery, sanitizePath, sanitizeToolOutput, scanInstructions, scanPluginDir, scanSkillDirs, searchMarketplaces, setDisplayCurrency, setImportGate, setPluginEnabled, setTrustGate, settingValueChoices, settingValueKind, settingWriteApply, settingWriteLayers, settingWriteTargets, skillIndexDescription, skillIndexResidency, skillIndexStats, sniffMediaType, splitFrontmatter, staticToolProvider, stripArtifactData, substitutePluginRoot, suggestRuleFromApproval, systemOpenArgv, systemPromptSegments, toPosix, toolDefinitionTokens, toolMatches, toolTableTokens, totalTokens, traceSession, triggerPeriodMs, unescapeRuleContent, uninstallPlugin, unknownRoleSkills, unknownRoleTools, unloadedInstructionFiles, updateMarketplace, updatePlugin, usdToMicros, validateSchedule, validateTrigger, validateWindow, withGoalCompression, withGoalPrompt, workspaceDisplayPath, writeSettingValue };