chore: remove dead code (59 unused files + ~125 unused exports) (#1835)
* chore: remove dead/unreferenced files across ui, vscode Remove 59 unused source files (components, hooks, lib utils, stores, barrels, and orphaned vscode github modules) that are not imported by any entry-reachable code. Also drop a stale test mock for the removed execCommands module. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * refactor: remove unused exported symbols (types, functions, consts, hooks) Remove exported symbols whose identifier is referenced nowhere in the repository (verified via repo-wide search), across ui types/contracts, lib utilities, sync layer, stores, and components. Also drop the few imports/private helpers orphaned by these removals. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * refactor: remove more unused exports (desktop, shortcuts, worktree, vscode) Continue removing repo-wide unreferenced exported functions, consts and types across lib/desktop, shortcuts, worktreeSessionCreator, sync, and vscode gitService, with cascading orphaned helpers/imports cleaned up. Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> * chore: add dead-code cleanup tooling * refactor: checkpoint dead-code cleanup * refactor: remove dead-code suppressions --------- Co-authored-by: Serhii Dziupin <makeittech@users.noreply.github.com> Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
Serhii Dziupin
Bohdan Triapitsyn
parent
4a37b9a005
commit
00821700de
@@ -10,10 +10,7 @@ import {
|
||||
intro,
|
||||
outro,
|
||||
log,
|
||||
note,
|
||||
box,
|
||||
progress,
|
||||
spinner,
|
||||
confirm,
|
||||
select,
|
||||
text,
|
||||
@@ -122,17 +119,13 @@ export {
|
||||
intro,
|
||||
outro,
|
||||
log,
|
||||
note,
|
||||
box,
|
||||
progress,
|
||||
spinner,
|
||||
confirm,
|
||||
select,
|
||||
text,
|
||||
password,
|
||||
cancel,
|
||||
isCancel,
|
||||
isTTY,
|
||||
isJsonMode,
|
||||
isQuietMode,
|
||||
shouldRenderHumanOutput,
|
||||
|
||||
@@ -41,7 +41,7 @@ export async function checkCloudflaredAvailable() {
|
||||
return { available: false, path: null, version: null };
|
||||
}
|
||||
|
||||
export function printCloudflareTunnelInstallHelp() {
|
||||
function printCloudflareTunnelInstallHelp() {
|
||||
const platform = process.platform;
|
||||
let installCmd = '';
|
||||
|
||||
@@ -600,7 +600,7 @@ export async function startCloudflareManagedLocalTunnel({ configPath, hostname }
|
||||
};
|
||||
}
|
||||
|
||||
export async function startCloudflareTunnel({ originUrl, port }) {
|
||||
async function startCloudflareTunnel({ originUrl, port }) {
|
||||
void port;
|
||||
return startCloudflareQuickTunnel({ originUrl });
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createUpstreamSseReader } from './upstream-reader.js';
|
||||
|
||||
// Raised from 512 → 2048 to improve recovery after brief disconnects during
|
||||
// long-running agent sessions where many events accumulate quickly.
|
||||
export const MESSAGE_STREAM_GLOBAL_REPLAY_LIMIT = 2048;
|
||||
const MESSAGE_STREAM_GLOBAL_REPLAY_LIMIT = 2048;
|
||||
|
||||
export function createGlobalMessageStreamHub({
|
||||
buildOpenCodeUrl,
|
||||
|
||||
@@ -1,25 +1,13 @@
|
||||
export {
|
||||
MESSAGE_STREAM_GLOBAL_WS_PATH,
|
||||
MESSAGE_STREAM_DIRECTORY_WS_PATH,
|
||||
MESSAGE_STREAM_WS_HEARTBEAT_INTERVAL_MS,
|
||||
parseSseEventEnvelope,
|
||||
sendMessageStreamWsFrame,
|
||||
sendMessageStreamWsEvent,
|
||||
} from './protocol.js';
|
||||
|
||||
export {
|
||||
createGlobalUiEventBroadcaster,
|
||||
createMessageStreamWsRuntime,
|
||||
} from './runtime.js';
|
||||
|
||||
export {
|
||||
MESSAGE_STREAM_GLOBAL_REPLAY_LIMIT,
|
||||
createGlobalMessageStreamHub,
|
||||
} from './global-hub.js';
|
||||
|
||||
export {
|
||||
DEFAULT_UPSTREAM_RECONNECT_DELAY_MS,
|
||||
DEFAULT_UPSTREAM_STALL_TIMEOUT_MS,
|
||||
UPSTREAM_STALL_TIMEOUT_CONCURRENT_MS,
|
||||
createUpstreamSseReader,
|
||||
} from './upstream-reader.js';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
import { parseRequestPathname } from '../terminal/index.js';
|
||||
import { parseRequestPathname } from '../terminal/terminal-ws-protocol.js';
|
||||
import {
|
||||
MESSAGE_STREAM_DIRECTORY_WS_PATH,
|
||||
MESSAGE_STREAM_GLOBAL_WS_PATH,
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
export { truncateNotificationText, prepareNotificationLastMessage } from './message.js';
|
||||
export { createNotificationTriggerRuntime } from './runtime.js';
|
||||
export { createPushRuntime } from './push-runtime.js';
|
||||
export { createNotificationTemplateRuntime } from './template-runtime.js';
|
||||
export { prepareNotificationLastMessage } from './message.js';
|
||||
|
||||
@@ -685,12 +685,6 @@ function deleteAgent(agentName, workingDirectory, scope) {
|
||||
}
|
||||
|
||||
export {
|
||||
ensureProjectAgentDir,
|
||||
getProjectAgentPath,
|
||||
getUserAgentPath,
|
||||
getAgentScope,
|
||||
getAgentWritePath,
|
||||
getAgentPermissionSource,
|
||||
getAgentSources,
|
||||
getAgentConfig,
|
||||
createAgent,
|
||||
|
||||
@@ -327,11 +327,6 @@ function deleteCommand(commandName, workingDirectory) {
|
||||
}
|
||||
|
||||
export {
|
||||
ensureProjectCommandDir,
|
||||
getProjectCommandPath,
|
||||
getUserCommandPath,
|
||||
getCommandScope,
|
||||
getCommandWritePath,
|
||||
getCommandSources,
|
||||
createCommand,
|
||||
updateCommand,
|
||||
|
||||
@@ -13,6 +13,33 @@ import { registerPluginRoutes } from './plugin-routes.js';
|
||||
import { getNpmInfo, clearCache as clearNpmCache } from './npm-registry.js';
|
||||
import { parseNpmSpec, parsePathSpec, isExactSemver } from './plugin-spec.js';
|
||||
import { registerOpenCodeRoutes } from './routes.js';
|
||||
import { getProviderSources, removeProviderConfig } from './providers.js';
|
||||
import { getAgentSources, getAgentConfig, createAgent, updateAgent, deleteAgent } from './agents.js';
|
||||
import { getCommandSources, createCommand, updateCommand, deleteCommand } from './commands.js';
|
||||
import { listMcpConfigs, getMcpConfig, createMcpConfig, updateMcpConfig, deleteMcpConfig } from './mcp.js';
|
||||
import { listSnippets, getSnippet, createSnippet, updateSnippet, deleteSnippet, expandSnippets } from './snippets.js';
|
||||
import {
|
||||
listPluginEntries,
|
||||
getPluginEntry,
|
||||
createPluginEntry,
|
||||
updatePluginEntry,
|
||||
deletePluginEntry,
|
||||
listPluginDirFiles,
|
||||
readPluginDirFile,
|
||||
writePluginDirFile,
|
||||
deletePluginDirFile,
|
||||
encodePluginId,
|
||||
decodePluginId,
|
||||
} from './plugins.js';
|
||||
import { SKILL_DIR, SKILL_SCOPE, readSkillSupportingFile, writeSkillSupportingFile, deleteSkillSupportingFile } from './shared.js';
|
||||
import { getSkillSources, discoverSkills, mergeDiscoveredSkills, createSkill, updateSkill, deleteSkill } from './skills.js';
|
||||
import { getCuratedSkillsSources } from '../skills-catalog/curated-sources.js';
|
||||
import { getCacheKey, getCachedScan, setCachedScan } from '../skills-catalog/cache.js';
|
||||
import { isClawdHubSource, parseSkillRepoSource } from '../skills-catalog/source.js';
|
||||
import { scanSkillsRepository } from '../skills-catalog/scan.js';
|
||||
import { installSkillsFromRepository } from '../skills-catalog/install.js';
|
||||
import { scanClawdHubPage } from '../skills-catalog/clawdhub/scan.js';
|
||||
import { installSkillsFromClawdHub } from '../skills-catalog/clawdhub/install.js';
|
||||
|
||||
export const createFeatureRoutesRuntime = (dependencies) => {
|
||||
const {
|
||||
@@ -63,8 +90,6 @@ export const createFeatureRoutesRuntime = (dependencies) => {
|
||||
writeSseEvent,
|
||||
} = routeDependencies;
|
||||
|
||||
const { getProviderSources, removeProviderConfig } = await import('./index.js');
|
||||
|
||||
registerSettingsUtilityRoutes(app, {
|
||||
readCustomThemesFromDisk,
|
||||
refreshOpenCodeAfterConfigChange,
|
||||
@@ -111,40 +136,6 @@ export const createFeatureRoutesRuntime = (dependencies) => {
|
||||
writeSseEvent,
|
||||
});
|
||||
|
||||
const {
|
||||
getAgentSources,
|
||||
getAgentConfig,
|
||||
createAgent,
|
||||
updateAgent,
|
||||
deleteAgent,
|
||||
getCommandSources,
|
||||
createCommand,
|
||||
updateCommand,
|
||||
deleteCommand,
|
||||
listMcpConfigs,
|
||||
getMcpConfig,
|
||||
createMcpConfig,
|
||||
updateMcpConfig,
|
||||
deleteMcpConfig,
|
||||
listSnippets,
|
||||
getSnippet,
|
||||
createSnippet,
|
||||
updateSnippet,
|
||||
deleteSnippet,
|
||||
expandSnippets,
|
||||
listPluginEntries,
|
||||
getPluginEntry,
|
||||
createPluginEntry,
|
||||
updatePluginEntry,
|
||||
deletePluginEntry,
|
||||
listPluginDirFiles,
|
||||
readPluginDirFile,
|
||||
writePluginDirFile,
|
||||
deletePluginDirFile,
|
||||
encodePluginId,
|
||||
decodePluginId,
|
||||
} = await import('./index.js');
|
||||
|
||||
registerConfigEntityRoutes(app, {
|
||||
resolveProjectDirectory,
|
||||
resolveOptionalProjectDirectory,
|
||||
@@ -193,32 +184,6 @@ export const createFeatureRoutesRuntime = (dependencies) => {
|
||||
isExactSemver,
|
||||
});
|
||||
|
||||
const {
|
||||
getSkillSources,
|
||||
discoverSkills,
|
||||
mergeDiscoveredSkills,
|
||||
createSkill,
|
||||
updateSkill,
|
||||
deleteSkill,
|
||||
readSkillSupportingFile,
|
||||
writeSkillSupportingFile,
|
||||
deleteSkillSupportingFile,
|
||||
SKILL_SCOPE,
|
||||
SKILL_DIR,
|
||||
} = await import('./index.js');
|
||||
|
||||
const {
|
||||
getCuratedSkillsSources,
|
||||
getCacheKey,
|
||||
getCachedScan,
|
||||
setCachedScan,
|
||||
parseSkillRepoSource,
|
||||
scanSkillsRepository,
|
||||
installSkillsFromRepository,
|
||||
scanClawdHubPage,
|
||||
installSkillsFromClawdHub,
|
||||
isClawdHubSource,
|
||||
} = await import('../skills-catalog/index.js');
|
||||
const { getProfiles, getProfile } = await import('../git/index.js');
|
||||
|
||||
registerSkillRoutes(app, {
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
export {
|
||||
AGENT_DIR,
|
||||
COMMAND_DIR,
|
||||
SKILL_DIR,
|
||||
CONFIG_FILE,
|
||||
AGENT_SCOPE,
|
||||
COMMAND_SCOPE,
|
||||
SKILL_SCOPE,
|
||||
readConfig,
|
||||
writeConfig,
|
||||
readSkillSupportingFile,
|
||||
writeSkillSupportingFile,
|
||||
deleteSkillSupportingFile,
|
||||
} from './shared.js';
|
||||
|
||||
export {
|
||||
getAgentScope,
|
||||
getAgentPermissionSource,
|
||||
getAgentSources,
|
||||
getAgentConfig,
|
||||
createAgent,
|
||||
updateAgent,
|
||||
deleteAgent,
|
||||
} from './agents.js';
|
||||
|
||||
export {
|
||||
getCommandScope,
|
||||
getCommandSources,
|
||||
createCommand,
|
||||
updateCommand,
|
||||
deleteCommand,
|
||||
} from './commands.js';
|
||||
|
||||
export {
|
||||
getSkillSources,
|
||||
getSkillScope,
|
||||
discoverSkills,
|
||||
mergeDiscoveredSkills,
|
||||
createSkill,
|
||||
updateSkill,
|
||||
deleteSkill,
|
||||
} from './skills.js';
|
||||
|
||||
export {
|
||||
getProviderSources,
|
||||
removeProviderConfig,
|
||||
} from './providers.js';
|
||||
|
||||
export {
|
||||
readAuthFile,
|
||||
writeAuthFile,
|
||||
removeProviderAuth,
|
||||
getProviderAuth,
|
||||
listProviderAuths,
|
||||
AUTH_FILE,
|
||||
OPENCODE_DATA_DIR,
|
||||
} from './auth.js';
|
||||
|
||||
export { createUiAuth } from '../ui-auth/ui-auth.js';
|
||||
|
||||
export {
|
||||
listMcpConfigs,
|
||||
getMcpConfig,
|
||||
createMcpConfig,
|
||||
updateMcpConfig,
|
||||
deleteMcpConfig,
|
||||
} from './mcp.js';
|
||||
|
||||
export {
|
||||
listPluginEntries,
|
||||
getPluginEntry,
|
||||
createPluginEntry,
|
||||
updatePluginEntry,
|
||||
deletePluginEntry,
|
||||
listPluginDirFiles,
|
||||
readPluginDirFile,
|
||||
writePluginDirFile,
|
||||
deletePluginDirFile,
|
||||
encodePluginId,
|
||||
decodePluginId,
|
||||
parsePluginRaw,
|
||||
serializePluginEntry,
|
||||
} from './plugins.js';
|
||||
|
||||
export {
|
||||
listSnippets,
|
||||
getSnippet,
|
||||
createSnippet,
|
||||
updateSnippet,
|
||||
deleteSnippet,
|
||||
expandSnippets,
|
||||
} from './snippets.js';
|
||||
|
||||
export { getNpmInfo, lookupNpmPackage, clearCache as clearNpmCache } from './npm-registry.js';
|
||||
export { parseNpmSpec, parsePathSpec, isExactSemver } from './plugin-spec.js';
|
||||
@@ -2,9 +2,9 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
export const NPM_CACHE_TTL_MS = 3_600_000;
|
||||
export const NPM_FETCH_TIMEOUT_MS = 5_000;
|
||||
export const NPM_REGISTRY_BASE = 'https://registry.npmjs.org';
|
||||
const NPM_CACHE_TTL_MS = 3_600_000;
|
||||
const NPM_FETCH_TIMEOUT_MS = 5_000;
|
||||
const NPM_REGISTRY_BASE = 'https://registry.npmjs.org';
|
||||
|
||||
/**
|
||||
* @typedef {Object} NpmPackagePayload
|
||||
|
||||
@@ -50,7 +50,7 @@ export const normalizeForwardedDirectoryHeaders = (headers) => {
|
||||
return headers;
|
||||
};
|
||||
|
||||
export const waitForSseDrain = (res, signal) => new Promise((resolve) => {
|
||||
const waitForSseDrain = (res, signal) => new Promise((resolve) => {
|
||||
if (signal?.aborted || res.writableEnded || res.destroyed) {
|
||||
resolve();
|
||||
return;
|
||||
@@ -132,7 +132,7 @@ const SESSION_LIST_ALLOWED_FIELDS = [
|
||||
'project',
|
||||
];
|
||||
|
||||
export const sanitizeSessionListItem = (session) => {
|
||||
const sanitizeSessionListItem = (session) => {
|
||||
if (!session || typeof session !== 'object' || Array.isArray(session)) {
|
||||
return session;
|
||||
}
|
||||
@@ -168,7 +168,7 @@ export const sanitizeSessionListItem = (session) => {
|
||||
return sanitized;
|
||||
};
|
||||
|
||||
export const sanitizeSessionListPayload = (payload) => {
|
||||
const sanitizeSessionListPayload = (payload) => {
|
||||
if (!Array.isArray(payload)) {
|
||||
return payload;
|
||||
}
|
||||
|
||||
@@ -507,20 +507,14 @@ export {
|
||||
COMMAND_DIR,
|
||||
SKILL_DIR,
|
||||
CONFIG_FILE,
|
||||
CUSTOM_CONFIG_FILE,
|
||||
PROMPT_FILE_PATTERN,
|
||||
AGENT_SCOPE,
|
||||
COMMAND_SCOPE,
|
||||
SKILL_SCOPE,
|
||||
ensureDirs,
|
||||
parseMdFile,
|
||||
writeMdFile,
|
||||
getProjectConfigCandidates,
|
||||
getProjectConfigPath,
|
||||
getConfigPaths,
|
||||
readConfigFile,
|
||||
isPlainObject,
|
||||
mergeConfigs,
|
||||
readConfigLayers,
|
||||
readConfig,
|
||||
getConfigForPath,
|
||||
|
||||
@@ -594,8 +594,6 @@ function deleteSkill(skillName, workingDirectory) {
|
||||
|
||||
export {
|
||||
getSkillSources,
|
||||
getSkillScope,
|
||||
getSkillWritePath,
|
||||
discoverSkills,
|
||||
mergeDiscoveredSkills,
|
||||
createSkill,
|
||||
|
||||
@@ -240,5 +240,3 @@ export function expandSnippets(text, workingDirectory) {
|
||||
const expanded = expandText(text || '', registry, new Map(), collector).trim();
|
||||
return [...collector.prepend, expanded, ...collector.append].filter(Boolean).join('\n\n');
|
||||
}
|
||||
|
||||
export { assertValidSnippetName };
|
||||
|
||||
@@ -483,7 +483,7 @@ export function detectPackageManagerDetails() {
|
||||
};
|
||||
}
|
||||
|
||||
export function detectPackageManager() {
|
||||
function detectPackageManager() {
|
||||
return detectPackageManagerDetails().packageManager;
|
||||
}
|
||||
|
||||
@@ -621,7 +621,7 @@ export function getUpdateCommand(pm = detectPackageManager()) {
|
||||
/**
|
||||
* Get current installed version from package.json
|
||||
*/
|
||||
export function getCurrentVersion() {
|
||||
function getCurrentVersion() {
|
||||
try {
|
||||
const pkgPath = path.resolve(__dirname, '..', '..', 'package.json');
|
||||
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
||||
@@ -634,7 +634,7 @@ export function getCurrentVersion() {
|
||||
/**
|
||||
* Fetch latest version from npm registry
|
||||
*/
|
||||
export async function getLatestVersion() {
|
||||
async function getLatestVersion() {
|
||||
try {
|
||||
const response = await fetch(NPM_REGISTRY_URL, {
|
||||
headers: { Accept: 'application/json' },
|
||||
@@ -690,7 +690,7 @@ function compareVersions(left, right) {
|
||||
/**
|
||||
* Fetch changelog notes between versions
|
||||
*/
|
||||
export async function fetchChangelogNotes(fromVersion, toVersion) {
|
||||
async function fetchChangelogNotes(fromVersion, toVersion) {
|
||||
try {
|
||||
const response = await fetch(CHANGELOG_URL, {
|
||||
signal: AbortSignal.timeout(10000),
|
||||
@@ -769,7 +769,7 @@ export async function checkForUpdates(options = {}) {
|
||||
/**
|
||||
* Execute the update (used by CLI)
|
||||
*/
|
||||
export function executeUpdate(pm = detectPackageManager(), options = {}) {
|
||||
function executeUpdate(pm = detectPackageManager(), options = {}) {
|
||||
const command = getUpdateCommand(pm);
|
||||
if (!options?.silent) {
|
||||
console.log(`Updating ${PACKAGE_NAME} using ${pm}...`);
|
||||
|
||||
@@ -557,11 +557,3 @@ export const createProjectConfigRuntime = (deps) => {
|
||||
resolveProjectConfigPath,
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
MAX_TASK_NAME_LENGTH,
|
||||
MAX_TASK_PROMPT_LENGTH,
|
||||
MAX_CRON_LENGTH,
|
||||
MAX_LAST_ERROR_LENGTH,
|
||||
normalizeTaskForStorage,
|
||||
};
|
||||
|
||||
@@ -7,7 +7,6 @@ This module fetches quota and usage signals for supported providers in the web s
|
||||
- `packages/web/server/lib/quota/index.js`: public entrypoint imported by `packages/web/server/index.js`.
|
||||
- `packages/web/server/lib/quota/routes.js`: Express route registration for quota endpoints.
|
||||
- `packages/web/server/lib/quota/providers/index.js`: provider registry, configured-provider list, and provider dispatcher.
|
||||
- `packages/web/server/lib/quota/providers/interface.js`: JSDoc provider contract used as implementation reference.
|
||||
- `packages/web/server/lib/quota/providers/google/`: Google-specific auth, API, and transform modules.
|
||||
- `packages/web/server/lib/quota/utils/`: shared auth, transform, and formatting helpers.
|
||||
|
||||
@@ -42,6 +41,9 @@ All providers should return results via shared helpers to preserve API shape:
|
||||
- Optional field: `error`
|
||||
- Unsupported provider requests should return `ok: false`, `configured: false`, `error: Unsupported provider`
|
||||
|
||||
Provider modules must export `providerId`, `providerName`, `aliases`, `isConfigured(auth?)`, and `fetchQuota()`.
|
||||
`fetchQuota()` should return a quota result with `usage.windows` keyed by window name (for example `5h`, `7d`, `daily`) and optional provider-specific `usage.models` data.
|
||||
|
||||
## Add a new provider (quick steps)
|
||||
1. Choose module shape based on complexity:
|
||||
- Simple providers: create `packages/web/server/lib/quota/providers/<provider>.js`.
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
|
||||
export const providerId = 'claude';
|
||||
export const providerName = 'Claude';
|
||||
export const aliases = ['anthropic', 'claude'];
|
||||
const aliases = ['anthropic', 'claude'];
|
||||
|
||||
export const isConfigured = () => {
|
||||
const auth = readAuthFile();
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
|
||||
export const providerId = 'codex';
|
||||
export const providerName = 'Codex';
|
||||
export const aliases = ['openai', 'codex', 'chatgpt'];
|
||||
const aliases = ['openai', 'codex', 'chatgpt'];
|
||||
|
||||
export const isConfigured = () => {
|
||||
const auth = readAuthFile();
|
||||
|
||||
@@ -40,7 +40,7 @@ const buildCopilotWindows = (payload) => {
|
||||
|
||||
export const providerId = 'github-copilot';
|
||||
export const providerName = 'GitHub Copilot';
|
||||
export const aliases = ['github-copilot', 'copilot'];
|
||||
const aliases = ['github-copilot', 'copilot'];
|
||||
|
||||
export const isConfigured = () => {
|
||||
const auth = readAuthFile();
|
||||
|
||||
@@ -21,7 +21,7 @@ const STATE_DB = join(homedir(), 'Library', 'Application Support', 'Cursor', 'Us
|
||||
|
||||
export const providerId = 'cursor';
|
||||
export const providerName = 'Cursor';
|
||||
export const aliases = ['cursor'];
|
||||
const aliases = ['cursor'];
|
||||
|
||||
const readJwtPayload = (token) => {
|
||||
try {
|
||||
|
||||
@@ -39,7 +39,7 @@ export const resolveGoogleOAuthClient = (sourceId) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const resolveGeminiCliAuth = (auth) => {
|
||||
const resolveGeminiCliAuth = (auth) => {
|
||||
const entry = normalizeAuthEntry(getAuthEntry(auth, ['google', 'google.oauth']));
|
||||
const entryObject = asObject(entry);
|
||||
if (!entryObject) {
|
||||
@@ -64,7 +64,7 @@ export const resolveGeminiCliAuth = (auth) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const resolveAntigravityAuth = () => {
|
||||
const resolveAntigravityAuth = () => {
|
||||
for (const filePath of ANTIGRAVITY_ACCOUNTS_PATHS) {
|
||||
const data = readJsonFile(filePath);
|
||||
const accounts = data?.accounts;
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
/**
|
||||
* Google Provider
|
||||
*
|
||||
* Google quota provider implementation.
|
||||
* @module quota/providers/google
|
||||
*/
|
||||
|
||||
export {
|
||||
resolveGoogleOAuthClient,
|
||||
resolveGeminiCliAuth,
|
||||
resolveAntigravityAuth,
|
||||
resolveGoogleAuthSources,
|
||||
DEFAULT_PROJECT_ID
|
||||
} from './auth.js';
|
||||
|
||||
export {
|
||||
resolveGoogleWindow,
|
||||
transformQuotaBucket,
|
||||
transformModelData
|
||||
} from './transforms.js';
|
||||
|
||||
export {
|
||||
refreshGoogleAccessToken,
|
||||
fetchGoogleQuotaBuckets,
|
||||
fetchGoogleModels
|
||||
} from './api.js';
|
||||
|
||||
import { buildResult } from '../../utils/index.js';
|
||||
import {
|
||||
resolveGoogleAuthSources,
|
||||
|
||||
@@ -29,7 +29,7 @@ export const parseGoogleRefreshToken = (rawRefreshToken) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const resolveGoogleWindow = (sourceId, resetAt) => {
|
||||
const resolveGoogleWindow = (sourceId, resetAt) => {
|
||||
if (sourceId === 'gemini') {
|
||||
return { label: 'daily', seconds: GOOGLE_DAILY_WINDOW_SECONDS };
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ export const fetchCopilotAddonQuota = copilot.fetchQuotaAddon;
|
||||
export const fetchKimiQuota = kimi.fetchQuota;
|
||||
export const fetchOpenRouterQuota = openrouter.fetchQuota;
|
||||
export const fetchZaiQuota = zai.fetchQuota;
|
||||
export const fetchZhipuaiCodingPlanQuota = zhipuaiCodingPlan.fetchQuota;
|
||||
const fetchZhipuaiCodingPlanQuota = zhipuaiCodingPlan.fetchQuota;
|
||||
export const fetchNanoGptQuota = nanogpt.fetchQuota;
|
||||
export const fetchMinimaxCodingPlanQuota = minimaxCodingPlan.fetchQuota;
|
||||
export const fetchMinimaxCnCodingPlanQuota = minimaxCnCodingPlan.fetchQuota;
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/**
|
||||
* Quota Provider Interface
|
||||
*
|
||||
* Defines the contract for implementing quota providers.
|
||||
* @module quota/providers
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} UsageWindow
|
||||
* @property {number|null} usedPercent - Percentage of usage (0-100)
|
||||
* @property {number|null} remainingPercent - Percentage remaining (0-100)
|
||||
* @property {number|null} windowSeconds - Window duration in seconds
|
||||
* @property {number|null} resetAfterSeconds - Seconds until reset
|
||||
* @property {number|null} resetAt - Unix timestamp when quota resets
|
||||
* @property {string|null} resetAtFormatted - Human-readable reset time
|
||||
* @property {string|null} resetAfterFormatted - Human-readable time until reset
|
||||
* @property {string|null} valueLabel - Optional label for display (e.g., "$10.00 remaining")
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ProviderUsage
|
||||
* @property {Object.<string, UsageWindow>} windows - Usage windows by key (e.g., '5h', '7d', 'daily')
|
||||
* @property {Object.<string, Object>} [models] - Model-specific usage (provider-specific)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} QuotaProviderResult
|
||||
* @property {string} providerId - Unique identifier for the provider
|
||||
* @property {string} providerName - Display name for the provider
|
||||
* @property {boolean} ok - Whether the fetch was successful
|
||||
* @property {boolean} configured - Whether the provider is configured
|
||||
* @property {ProviderUsage|null} usage - Usage data if successful
|
||||
* @property {string|null} [error] - Error message if not successful
|
||||
* @property {number} fetchedAt - Unix timestamp when the result was fetched
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Function} ProviderQuotaFetcher
|
||||
* @returns {Promise<QuotaProviderResult>}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Function} ProviderConfigurationChecker
|
||||
* @param {Object.<string, unknown>} [auth]
|
||||
* @returns {boolean}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} QuotaProvider
|
||||
* @property {string} providerId
|
||||
* @property {string} providerName
|
||||
* @property {string[]} aliases
|
||||
* @property {ProviderConfigurationChecker} isConfigured
|
||||
* @property {ProviderQuotaFetcher} fetchQuota
|
||||
*/
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
|
||||
export const providerId = 'kimi-for-coding';
|
||||
export const providerName = 'Kimi for Coding';
|
||||
export const aliases = ['kimi-for-coding', 'kimi'];
|
||||
const aliases = ['kimi-for-coding', 'kimi'];
|
||||
|
||||
export const isConfigured = () => {
|
||||
const auth = readAuthFile();
|
||||
|
||||
@@ -10,6 +10,6 @@ const provider = createMiniMaxCodingPlanProvider({
|
||||
|
||||
export const providerId = provider.providerId;
|
||||
export const providerName = provider.providerName;
|
||||
export const aliases = provider.aliases;
|
||||
const aliases = provider.aliases;
|
||||
export const isConfigured = provider.isConfigured;
|
||||
export const fetchQuota = provider.fetchQuota;
|
||||
|
||||
@@ -10,6 +10,6 @@ const provider = createMiniMaxCodingPlanProvider({
|
||||
|
||||
export const providerId = provider.providerId;
|
||||
export const providerName = provider.providerName;
|
||||
export const aliases = provider.aliases;
|
||||
const aliases = provider.aliases;
|
||||
export const isConfigured = provider.isConfigured;
|
||||
export const fetchQuota = provider.fetchQuota;
|
||||
|
||||
@@ -12,7 +12,7 @@ const NANO_GPT_DAILY_WINDOW_SECONDS = 86400;
|
||||
|
||||
export const providerId = 'nano-gpt';
|
||||
export const providerName = 'NanoGPT';
|
||||
export const aliases = ['nano-gpt', 'nanogpt', 'nano_gpt'];
|
||||
const aliases = ['nano-gpt', 'nanogpt', 'nano_gpt'];
|
||||
|
||||
export const isConfigured = () => {
|
||||
const auth = readAuthFile();
|
||||
|
||||
@@ -7,7 +7,7 @@ const COOKIE_PATH = join(homedir(), '.config', 'ollama-quota', 'cookie');
|
||||
|
||||
export const providerId = 'ollama-cloud';
|
||||
export const providerName = 'Ollama Cloud';
|
||||
export const aliases = ['ollama-cloud', 'ollamacloud'];
|
||||
const aliases = ['ollama-cloud', 'ollamacloud'];
|
||||
|
||||
const readCookieFile = () => {
|
||||
try {
|
||||
|
||||
@@ -8,11 +8,11 @@ import {
|
||||
toTimestamp
|
||||
} from '../utils/index.js';
|
||||
|
||||
export const providerId = 'openai';
|
||||
export const providerName = 'OpenAI';
|
||||
export const aliases = ['openai', 'codex', 'chatgpt'];
|
||||
const providerId = 'openai';
|
||||
const providerName = 'OpenAI';
|
||||
const aliases = ['openai', 'codex', 'chatgpt'];
|
||||
|
||||
export const isConfigured = () => {
|
||||
const isConfigured = () => {
|
||||
const auth = readAuthFile();
|
||||
const entry = normalizeAuthEntry(getAuthEntry(auth, aliases));
|
||||
return Boolean(entry?.access || entry?.token);
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
|
||||
export const providerId = 'openrouter';
|
||||
export const providerName = 'OpenRouter';
|
||||
export const aliases = ['openrouter'];
|
||||
const aliases = ['openrouter'];
|
||||
|
||||
export const isConfigured = () => {
|
||||
const auth = readAuthFile();
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
|
||||
export const providerId = 'wafer';
|
||||
export const providerName = 'Wafer.ai';
|
||||
export const aliases = ['wafer', 'wafer-ai', 'wafer_ai', 'wafer.ai'];
|
||||
const aliases = ['wafer', 'wafer-ai', 'wafer_ai', 'wafer.ai'];
|
||||
|
||||
const WAFER_QUOTA_URL = 'https://pass.wafer.ai/v1/inference/quota';
|
||||
const WAFER_WINDOW_SECONDS = 5 * 3600;
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
|
||||
export const providerId = 'zai-coding-plan';
|
||||
export const providerName = 'z.ai';
|
||||
export const aliases = ['zai-coding-plan', 'zai', 'z.ai'];
|
||||
const aliases = ['zai-coding-plan', 'zai', 'z.ai'];
|
||||
|
||||
export const isConfigured = () => {
|
||||
const auth = readAuthFile();
|
||||
|
||||
@@ -38,7 +38,7 @@ import {
|
||||
|
||||
export const providerId = 'zhipuai-coding-plan';
|
||||
export const providerName = 'Zhipu AI Coding Plan';
|
||||
export const aliases = ['zhipuai-coding-plan', 'zhipuai', 'zhipu'];
|
||||
const aliases = ['zhipuai-coding-plan', 'zhipuai', 'zhipu'];
|
||||
|
||||
function getApiKey() {
|
||||
const auth = readAuthFile();
|
||||
|
||||
@@ -23,7 +23,3 @@ export function setCachedScan(key, value, ttlMs = DEFAULT_TTL_MS) {
|
||||
const ttl = Number.isFinite(ttlMs) ? ttlMs : DEFAULT_TTL_MS;
|
||||
cache.set(key, { expiresAt: Date.now() + ttl, value });
|
||||
}
|
||||
|
||||
export function clearCache() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
@@ -82,38 +82,6 @@ export async function fetchClawdHubSkills({ cursor } = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch details for a specific skill version
|
||||
* @param {string} slug - Skill slug/identifier
|
||||
* @param {string} [version='latest'] - Version string or 'latest'
|
||||
* @returns {Promise<{ skill: Object, version: Object }>}
|
||||
*/
|
||||
export async function fetchClawdHubSkillVersion(slug, version = 'latest') {
|
||||
// For 'latest', we need to first get the skill metadata to find the latest version
|
||||
if (version === 'latest') {
|
||||
const skillResponse = await rateLimitedFetch(`${CLAWDHUB_API_BASE}/skills/${encodeURIComponent(slug)}`);
|
||||
if (!skillResponse.ok) {
|
||||
throw new Error(`ClawdHub skill not found: ${slug}`);
|
||||
}
|
||||
const skillData = await skillResponse.json();
|
||||
const latestVersion = skillData.skill?.tags?.latest || skillData.latestVersion?.version;
|
||||
if (!latestVersion) {
|
||||
throw new Error(`No latest version found for skill: ${slug}`);
|
||||
}
|
||||
version = latestVersion;
|
||||
}
|
||||
|
||||
const url = `${CLAWDHUB_API_BASE}/skills/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version)}`;
|
||||
const response = await rateLimitedFetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text().catch(() => '');
|
||||
throw new Error(`ClawdHub version error (${response.status}): ${text || response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a skill package as a ZIP buffer
|
||||
* @param {string} slug - Skill slug/identifier
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* ClawdHub integration module
|
||||
*
|
||||
* Provides skill browsing and installation from the ClawdHub registry.
|
||||
* https://clawdhub.com
|
||||
*/
|
||||
|
||||
export { scanClawdHub, scanClawdHubPage } from './scan.js';
|
||||
export { installSkillsFromClawdHub } from './install.js';
|
||||
export {
|
||||
fetchClawdHubSkills,
|
||||
fetchClawdHubSkillVersion,
|
||||
fetchClawdHubSkillInfo,
|
||||
downloadClawdHubSkill,
|
||||
} from './api.js';
|
||||
|
||||
/**
|
||||
* Check if a source string refers to ClawdHub
|
||||
* @param {string} source
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isClawdHubSource(source) {
|
||||
return typeof source === 'string' && source.startsWith('clawdhub:');
|
||||
}
|
||||
|
||||
/**
|
||||
* ClawdHub source identifier used in curated sources
|
||||
*/
|
||||
export const CLAWDHUB_SOURCE_ID = 'clawdhub';
|
||||
export const CLAWDHUB_SOURCE_STRING = 'clawdhub:registry';
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
import { fetchClawdHubSkills } from './api.js';
|
||||
|
||||
const MAX_PAGES = 20; // Safety limit to prevent infinite loops
|
||||
const CLAWDHUB_PAGE_LIMIT = 25;
|
||||
|
||||
const mapClawdHubItem = (item) => {
|
||||
@@ -39,57 +38,6 @@ const mapClawdHubItem = (item) => {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Scan ClawdHub registry for all available skills
|
||||
* @returns {Promise<{ ok: boolean, items?: Array, error?: Object }>}
|
||||
*/
|
||||
export async function scanClawdHub() {
|
||||
try {
|
||||
const allItems = [];
|
||||
let cursor = null;
|
||||
|
||||
for (let page = 0; page < MAX_PAGES; page++) {
|
||||
let items = [];
|
||||
let nextCursor = null;
|
||||
|
||||
try {
|
||||
const pageResult = await fetchClawdHubSkills({ cursor });
|
||||
items = pageResult.items || [];
|
||||
nextCursor = pageResult.nextCursor || null;
|
||||
} catch (error) {
|
||||
if (page > 0 && allItems.length > 0) {
|
||||
console.warn('ClawdHub pagination failed; returning partial results.');
|
||||
break;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
for (const item of items) {
|
||||
allItems.push(mapClawdHubItem(item));
|
||||
}
|
||||
|
||||
if (!nextCursor) {
|
||||
break;
|
||||
}
|
||||
cursor = nextCursor;
|
||||
}
|
||||
|
||||
// Sort by downloads (most popular first)
|
||||
allItems.sort((a, b) => (b.clawdhub?.downloads || 0) - (a.clawdhub?.downloads || 0));
|
||||
|
||||
return { ok: true, items: allItems };
|
||||
} catch (error) {
|
||||
console.error('ClawdHub scan error:', error);
|
||||
return {
|
||||
ok: false,
|
||||
error: {
|
||||
kind: 'networkError',
|
||||
message: error instanceof Error ? error.message : 'Failed to fetch skills from ClawdHub',
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan a single ClawdHub page (cursor-based)
|
||||
* @returns {Promise<{ ok: boolean, items?: Array, nextCursor?: string | null, error?: Object }>}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const CURATED_SKILLS_SOURCES = [
|
||||
const CURATED_SKILLS_SOURCES = [
|
||||
{
|
||||
id: 'anthropic',
|
||||
label: 'Anthropic',
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Skills catalog module
|
||||
*
|
||||
* Provides skill scanning, installation, and caching from GitHub repositories and ClawdHub.
|
||||
*/
|
||||
|
||||
export {
|
||||
CURATED_SKILLS_SOURCES,
|
||||
getCuratedSkillsSources,
|
||||
} from './curated-sources.js';
|
||||
|
||||
export {
|
||||
getCacheKey,
|
||||
getCachedScan,
|
||||
setCachedScan,
|
||||
clearCache,
|
||||
} from './cache.js';
|
||||
|
||||
export {
|
||||
parseSkillRepoSource,
|
||||
} from './source.js';
|
||||
|
||||
export {
|
||||
scanSkillsRepository,
|
||||
} from './scan.js';
|
||||
|
||||
export {
|
||||
installSkillsFromRepository,
|
||||
} from './install.js';
|
||||
|
||||
export {
|
||||
scanClawdHub,
|
||||
scanClawdHubPage,
|
||||
installSkillsFromClawdHub,
|
||||
fetchClawdHubSkills,
|
||||
fetchClawdHubSkillVersion,
|
||||
fetchClawdHubSkillInfo,
|
||||
downloadClawdHubSkill,
|
||||
isClawdHubSource,
|
||||
CLAWDHUB_SOURCE_ID,
|
||||
CLAWDHUB_SOURCE_STRING,
|
||||
} from './clawdhub/index.js';
|
||||
@@ -1,4 +1,5 @@
|
||||
const GITHUB_HOST = 'github.com';
|
||||
const CLAWDHUB_SOURCE_PREFIX = 'clawdhub:';
|
||||
|
||||
|
||||
function normalizeGitOwnerRepo(owner, repo) {
|
||||
@@ -85,3 +86,7 @@ export function parseSkillRepoSource(input, options = {}) {
|
||||
|
||||
return { ok: false, error: { kind: 'invalidSource', message: 'Unsupported repository source format' } };
|
||||
}
|
||||
|
||||
export function isClawdHubSource(input) {
|
||||
return typeof input === 'string' && input.trim().toLowerCase().startsWith(CLAWDHUB_SOURCE_PREFIX);
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
export {
|
||||
TERMINAL_WS_PATH,
|
||||
TERMINAL_WS_CONTROL_TAG_JSON,
|
||||
TERMINAL_WS_MAX_PAYLOAD_BYTES,
|
||||
isTerminalWsPathname,
|
||||
parseRequestPathname,
|
||||
normalizeTerminalWsMessageToBuffer,
|
||||
normalizeTerminalWsMessageToText,
|
||||
readTerminalWsControlFrame,
|
||||
createTerminalWsControlFrame,
|
||||
pruneRebindTimestamps,
|
||||
isRebindRateLimited,
|
||||
} from './terminal-ws-protocol.js';
|
||||
|
||||
export {
|
||||
TERMINAL_WS_PATH as TERMINAL_INPUT_WS_PATH,
|
||||
TERMINAL_WS_CONTROL_TAG_JSON as TERMINAL_INPUT_WS_CONTROL_TAG_JSON,
|
||||
TERMINAL_WS_MAX_PAYLOAD_BYTES as TERMINAL_INPUT_WS_MAX_PAYLOAD_BYTES,
|
||||
normalizeTerminalWsMessageToBuffer as normalizeTerminalInputWsMessageToBuffer,
|
||||
normalizeTerminalWsMessageToText as normalizeTerminalInputWsMessageToText,
|
||||
readTerminalWsControlFrame as readTerminalInputWsControlFrame,
|
||||
createTerminalWsControlFrame as createTerminalInputWsControlFrame,
|
||||
} from './terminal-ws-protocol.js';
|
||||
|
||||
export {
|
||||
TERMINAL_OUTPUT_REPLAY_MAX_BYTES,
|
||||
createTerminalOutputReplayBuffer,
|
||||
appendTerminalOutputReplayChunk,
|
||||
listTerminalOutputReplayChunksSince,
|
||||
getLatestTerminalOutputReplayChunkId,
|
||||
} from './output-replay-buffer.js';
|
||||
@@ -1,18 +1,20 @@
|
||||
import { WebSocketServer } from 'ws';
|
||||
import {
|
||||
TERMINAL_INPUT_WS_MAX_PAYLOAD_BYTES,
|
||||
TERMINAL_INPUT_WS_PATH,
|
||||
TERMINAL_WS_MAX_PAYLOAD_BYTES as TERMINAL_INPUT_WS_MAX_PAYLOAD_BYTES,
|
||||
TERMINAL_WS_PATH as TERMINAL_INPUT_WS_PATH,
|
||||
createTerminalWsControlFrame as createTerminalInputWsControlFrame,
|
||||
isRebindRateLimited,
|
||||
normalizeTerminalWsMessageToText as normalizeTerminalInputWsMessageToText,
|
||||
parseRequestPathname,
|
||||
pruneRebindTimestamps,
|
||||
readTerminalWsControlFrame as readTerminalInputWsControlFrame,
|
||||
} from './terminal-ws-protocol.js';
|
||||
import {
|
||||
TERMINAL_OUTPUT_REPLAY_MAX_BYTES,
|
||||
appendTerminalOutputReplayChunk,
|
||||
createTerminalOutputReplayBuffer,
|
||||
createTerminalInputWsControlFrame,
|
||||
isRebindRateLimited,
|
||||
listTerminalOutputReplayChunksSince,
|
||||
normalizeTerminalInputWsMessageToText,
|
||||
parseRequestPathname,
|
||||
pruneRebindTimestamps,
|
||||
readTerminalInputWsControlFrame,
|
||||
} from './index.js';
|
||||
} from './output-replay-buffer.js';
|
||||
|
||||
export function createTerminalRuntime({
|
||||
app,
|
||||
|
||||
@@ -25,7 +25,7 @@ export function sanitizeForTTS(text) {
|
||||
.trim();
|
||||
}
|
||||
|
||||
export function sanitizeForNotification(text) {
|
||||
function sanitizeForNotification(text) {
|
||||
if (!text || typeof text !== 'string') return '';
|
||||
|
||||
return text
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
} from '../types.js';
|
||||
import { getTunnelDependencyInstallInfo } from '../install-help.js';
|
||||
|
||||
export const ngrokTunnelProviderCapabilities = {
|
||||
const ngrokTunnelProviderCapabilities = {
|
||||
provider: TUNNEL_PROVIDER_NGROK,
|
||||
defaults: {
|
||||
mode: TUNNEL_MODE_QUICK,
|
||||
|
||||
@@ -10,7 +10,7 @@ export const TUNNEL_MODE_MANAGED_LOCAL = 'managed-local';
|
||||
|
||||
export const TUNNEL_INTENT_EPHEMERAL_PUBLIC = 'ephemeral-public';
|
||||
export const TUNNEL_INTENT_PERSISTENT_PUBLIC = 'persistent-public';
|
||||
export const TUNNEL_INTENT_PRIVATE_NETWORK = 'private-network';
|
||||
const TUNNEL_INTENT_PRIVATE_NETWORK = 'private-network';
|
||||
|
||||
const SUPPORTED_TUNNEL_INTENTS = new Set([
|
||||
TUNNEL_INTENT_EPHEMERAL_PUBLIC,
|
||||
@@ -108,7 +108,7 @@ export function normalizeTunnelMode(value) {
|
||||
return TUNNEL_MODE_QUICK;
|
||||
}
|
||||
|
||||
export function normalizeTunnelIntent(value) {
|
||||
function normalizeTunnelIntent(value) {
|
||||
if (typeof value !== 'string') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user