@i4ctime/q-ring 0.3.1 → 0.4.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.
- package/README.md +196 -6
- package/dist/{chunk-F4SPZ774.js → chunk-6IQ5SFLI.js} +298 -6
- package/dist/chunk-6IQ5SFLI.js.map +1 -0
- package/dist/{chunk-3WTTWJYU.js → chunk-IGNU622R.js} +337 -5
- package/dist/chunk-IGNU622R.js.map +1 -0
- package/dist/{dashboard-X3ONQFLV.js → dashboard-32PCZF7D.js} +2 -2
- package/dist/{dashboard-QQWKOOI5.js → dashboard-HVIQO6NT.js} +2 -2
- package/dist/index.js +739 -53
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +580 -9
- package/dist/mcp.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-3WTTWJYU.js.map +0 -1
- package/dist/chunk-F4SPZ774.js.map +0 -1
- /package/dist/{dashboard-QQWKOOI5.js.map → dashboard-32PCZF7D.js.map} +0 -0
- /package/dist/{dashboard-X3ONQFLV.js.map → dashboard-HVIQO6NT.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/envelope.ts","../src/core/collapse.ts","../src/core/observer.ts","../src/core/entanglement.ts","../src/core/keyring.ts","../src/utils/hash.ts","../src/core/scope.ts","../src/core/hooks.ts","../src/core/tunnel.ts"],"sourcesContent":["/**\n * Quantum Envelope: the storage format for all q-ring secrets.\n *\n * Instead of storing raw strings, every secret is wrapped in an envelope\n * that carries quantum metadata: environment states (superposition),\n * TTL/expiry (decay), entanglement links, and access tracking (observer).\n */\n\nexport type Environment = string; // \"dev\" | \"staging\" | \"prod\" | custom\n\nexport interface EntanglementLink {\n /** Service name of the entangled scope */\n service: string;\n /** Key name in the entangled scope */\n key: string;\n}\n\nexport interface SecretMetadata {\n createdAt: string;\n updatedAt: string;\n /** ISO timestamp when this secret expires (quantum decay) */\n expiresAt?: string;\n /** TTL in seconds from creation/update */\n ttlSeconds?: number;\n /** Human-readable description */\n description?: string;\n /** Tags for organization */\n tags?: string[];\n /** Entanglement links to other secrets */\n entangled?: EntanglementLink[];\n /** Total number of times this secret has been read */\n accessCount: number;\n /** ISO timestamp of last read */\n lastAccessedAt?: string;\n /** Whether this secret is ephemeral (tunneling - not persisted to keyring) */\n ephemeral?: boolean;\n /** Format to use when auto-rotating (e.g. \"api-key\", \"password\", \"uuid\") */\n rotationFormat?: string;\n /** Prefix to use when auto-rotating api-key/token formats */\n rotationPrefix?: string;\n /** Provider name for liveness validation (e.g. \"openai\", \"stripe\", \"github\") */\n provider?: string;\n}\n\nexport interface QuantumEnvelope {\n /** Schema version for forward compatibility */\n v: 1;\n /** Simple value (when not in superposition) */\n value?: string;\n /** Superposition: environment-keyed values */\n states?: Record<Environment, string>;\n /** Default environment to collapse to when no context is available */\n defaultEnv?: Environment;\n /** Quantum metadata */\n meta: SecretMetadata;\n}\n\nexport function createEnvelope(\n value: string,\n opts?: Partial<Pick<QuantumEnvelope, \"states\" | \"defaultEnv\">> & {\n description?: string;\n tags?: string[];\n ttlSeconds?: number;\n expiresAt?: string;\n entangled?: EntanglementLink[];\n rotationFormat?: string;\n rotationPrefix?: string;\n provider?: string;\n },\n): QuantumEnvelope {\n const now = new Date().toISOString();\n\n let expiresAt = opts?.expiresAt;\n if (!expiresAt && opts?.ttlSeconds) {\n expiresAt = new Date(Date.now() + opts.ttlSeconds * 1000).toISOString();\n }\n\n return {\n v: 1,\n value: opts?.states ? undefined : value,\n states: opts?.states,\n defaultEnv: opts?.defaultEnv,\n meta: {\n createdAt: now,\n updatedAt: now,\n expiresAt,\n ttlSeconds: opts?.ttlSeconds,\n description: opts?.description,\n tags: opts?.tags,\n entangled: opts?.entangled,\n accessCount: 0,\n rotationFormat: opts?.rotationFormat,\n rotationPrefix: opts?.rotationPrefix,\n provider: opts?.provider,\n },\n };\n}\n\nexport function parseEnvelope(raw: string): QuantumEnvelope | null {\n try {\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\" && parsed.v === 1) {\n return parsed as QuantumEnvelope;\n }\n } catch {\n // Not a quantum envelope - legacy raw string\n }\n return null;\n}\n\n/**\n * Wrap a legacy raw string value into a quantum envelope.\n * Used for backward compatibility with secrets stored before the envelope format.\n */\nexport function wrapLegacy(rawValue: string): QuantumEnvelope {\n const now = new Date().toISOString();\n return {\n v: 1,\n value: rawValue,\n meta: {\n createdAt: now,\n updatedAt: now,\n accessCount: 0,\n },\n };\n}\n\nexport function serializeEnvelope(envelope: QuantumEnvelope): string {\n return JSON.stringify(envelope);\n}\n\n/**\n * Resolve the concrete value from a quantum envelope.\n * If in superposition, collapses based on the provided environment.\n */\nexport function collapseValue(\n envelope: QuantumEnvelope,\n env?: Environment,\n): string | null {\n if (envelope.states) {\n const targetEnv = env ?? envelope.defaultEnv;\n if (targetEnv && envelope.states[targetEnv]) {\n return envelope.states[targetEnv];\n }\n // If no env match, try default, then return null\n if (envelope.defaultEnv && envelope.states[envelope.defaultEnv]) {\n return envelope.states[envelope.defaultEnv];\n }\n // Last resort: return the first state\n const keys = Object.keys(envelope.states);\n if (keys.length > 0) {\n return envelope.states[keys[0]];\n }\n return null;\n }\n\n return envelope.value ?? null;\n}\n\nexport interface DecayStatus {\n isExpired: boolean;\n isStale: boolean;\n /** Percentage of lifetime elapsed (0-100+) */\n lifetimePercent: number;\n /** Seconds remaining until expiry, or negative if expired */\n secondsRemaining: number | null;\n /** Human-readable time remaining */\n timeRemaining: string | null;\n}\n\nexport function checkDecay(envelope: QuantumEnvelope): DecayStatus {\n if (!envelope.meta.expiresAt) {\n return {\n isExpired: false,\n isStale: false,\n lifetimePercent: 0,\n secondsRemaining: null,\n timeRemaining: null,\n };\n }\n\n const now = Date.now();\n const expires = new Date(envelope.meta.expiresAt).getTime();\n const created = new Date(envelope.meta.createdAt).getTime();\n const totalLifetime = expires - created;\n const elapsed = now - created;\n const remaining = expires - now;\n\n const lifetimePercent =\n totalLifetime > 0 ? Math.round((elapsed / totalLifetime) * 100) : 100;\n\n const secondsRemaining = Math.floor(remaining / 1000);\n\n let timeRemaining: string | null = null;\n if (remaining > 0) {\n const days = Math.floor(remaining / 86400000);\n const hours = Math.floor((remaining % 86400000) / 3600000);\n const minutes = Math.floor((remaining % 3600000) / 60000);\n\n if (days > 0) timeRemaining = `${days}d ${hours}h`;\n else if (hours > 0) timeRemaining = `${hours}h ${minutes}m`;\n else timeRemaining = `${minutes}m`;\n } else {\n timeRemaining = \"expired\";\n }\n\n return {\n isExpired: remaining <= 0,\n isStale: lifetimePercent >= 75,\n lifetimePercent,\n secondsRemaining,\n timeRemaining,\n };\n}\n\n/**\n * Record an access event on the envelope (observer effect).\n * Returns a new envelope with updated access metadata.\n */\nexport function recordAccess(envelope: QuantumEnvelope): QuantumEnvelope {\n return {\n ...envelope,\n meta: {\n ...envelope.meta,\n accessCount: envelope.meta.accessCount + 1,\n lastAccessedAt: new Date().toISOString(),\n },\n };\n}\n","/**\n * Wavefunction Collapse: auto-detect the current environment context.\n *\n * Resolution order (first match wins):\n * 1. Explicit --env flag\n * 2. QRING_ENV environment variable\n * 3. NODE_ENV environment variable\n * 4. Git branch heuristics (main/master → prod, develop → dev, staging → staging)\n * 5. .q-ring.json project config\n * 6. Default environment from the envelope\n */\n\nimport { execSync } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { Environment } from \"./envelope.js\";\n\nconst BRANCH_ENV_MAP: Record<string, Environment> = {\n main: \"prod\",\n master: \"prod\",\n production: \"prod\",\n develop: \"dev\",\n development: \"dev\",\n dev: \"dev\",\n staging: \"staging\",\n stage: \"staging\",\n test: \"test\",\n testing: \"test\",\n};\n\nfunction detectGitBranch(cwd?: string): string | null {\n try {\n const branch = execSync(\"git rev-parse --abbrev-ref HEAD\", {\n cwd: cwd ?? process.cwd(),\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n encoding: \"utf8\",\n timeout: 3000,\n }).trim();\n return branch || null;\n } catch {\n return null;\n }\n}\n\nexport interface ManifestEntry {\n required?: boolean;\n description?: string;\n /** Expected format for auto-rotation (e.g. \"api-key\", \"password\", \"uuid\") */\n format?: string;\n /** Expected prefix (e.g. \"sk-\") */\n prefix?: string;\n /** Provider name for liveness validation (e.g. \"openai\", \"stripe\", \"github\") */\n provider?: string;\n /** Custom validation URL for generic HTTP provider */\n validationUrl?: string;\n}\n\nexport interface ProjectConfig {\n env?: Environment;\n defaultEnv?: Environment;\n branchMap?: Record<string, Environment>;\n /** Secrets manifest — declares required/expected secrets for this project */\n secrets?: Record<string, ManifestEntry>;\n}\n\nexport function readProjectConfig(projectPath?: string): ProjectConfig | null {\n const configPath = join(projectPath ?? process.cwd(), \".q-ring.json\");\n try {\n if (existsSync(configPath)) {\n return JSON.parse(readFileSync(configPath, \"utf8\"));\n }\n } catch {\n // invalid config\n }\n return null;\n}\n\nexport interface CollapseContext {\n /** Explicitly provided environment */\n explicit?: Environment;\n /** Project path for git/config detection */\n projectPath?: string;\n}\n\nexport interface CollapseResult {\n env: Environment;\n source:\n | \"explicit\"\n | \"QRING_ENV\"\n | \"NODE_ENV\"\n | \"git-branch\"\n | \"project-config\"\n | \"default\";\n}\n\nexport function collapseEnvironment(\n ctx: CollapseContext = {},\n): CollapseResult | null {\n if (ctx.explicit) {\n return { env: ctx.explicit, source: \"explicit\" };\n }\n\n const qringEnv = process.env.QRING_ENV;\n if (qringEnv) {\n return { env: qringEnv, source: \"QRING_ENV\" };\n }\n\n const nodeEnv = process.env.NODE_ENV;\n if (nodeEnv) {\n const mapped = mapEnvName(nodeEnv);\n return { env: mapped, source: \"NODE_ENV\" };\n }\n\n const config = readProjectConfig(ctx.projectPath);\n if (config?.env) {\n return { env: config.env, source: \"project-config\" };\n }\n\n const branch = detectGitBranch(ctx.projectPath);\n if (branch) {\n const branchMap = { ...BRANCH_ENV_MAP, ...config?.branchMap };\n const mapped = branchMap[branch] ?? matchGlob(branchMap, branch);\n if (mapped) {\n return { env: mapped, source: \"git-branch\" };\n }\n }\n\n if (config?.defaultEnv) {\n return { env: config.defaultEnv, source: \"project-config\" };\n }\n\n return null;\n}\n\n/**\n * Match a branch name against glob-style patterns in the branchMap.\n * Supports `*` as a wildcard (e.g., `release/*`, `feature/*`).\n */\nfunction matchGlob(\n branchMap: Record<string, Environment>,\n branch: string,\n): Environment | undefined {\n for (const [pattern, env] of Object.entries(branchMap)) {\n if (!pattern.includes(\"*\")) continue;\n const regex = new RegExp(\n \"^\" + pattern.replace(/\\*/g, \".*\") + \"$\",\n );\n if (regex.test(branch)) return env;\n }\n return undefined;\n}\n\nfunction mapEnvName(raw: string): Environment {\n const lower = raw.toLowerCase();\n if (lower === \"production\") return \"prod\";\n if (lower === \"development\") return \"dev\";\n return lower;\n}\n","/**\n * Observer Effect: every secret read/write/delete is logged.\n * Audit trail stored at ~/.config/q-ring/audit.jsonl\n *\n * The act of observation changes the state — each access increments\n * the envelope's access counter and records a timestamp.\n */\n\nimport { existsSync, mkdirSync, appendFileSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nexport type AuditAction =\n | \"read\"\n | \"write\"\n | \"delete\"\n | \"list\"\n | \"export\"\n | \"generate\"\n | \"entangle\"\n | \"tunnel\"\n | \"teleport\"\n | \"collapse\";\n\nexport interface AuditEvent {\n timestamp: string;\n action: AuditAction;\n key?: string;\n scope?: string;\n env?: string;\n source: \"cli\" | \"mcp\" | \"agent\" | \"api\";\n /** Additional context */\n detail?: string;\n /** Process ID for tracking */\n pid: number;\n}\n\nfunction getAuditDir(): string {\n const dir = join(homedir(), \".config\", \"q-ring\");\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n return dir;\n}\n\nfunction getAuditPath(): string {\n return join(getAuditDir(), \"audit.jsonl\");\n}\n\nexport function logAudit(event: Omit<AuditEvent, \"timestamp\" | \"pid\">): void {\n const full: AuditEvent = {\n ...event,\n timestamp: new Date().toISOString(),\n pid: process.pid,\n };\n\n try {\n appendFileSync(getAuditPath(), JSON.stringify(full) + \"\\n\");\n } catch {\n // audit logging should never crash the app\n }\n}\n\nexport interface AuditQuery {\n key?: string;\n action?: AuditAction;\n since?: string;\n limit?: number;\n}\n\nexport function queryAudit(query: AuditQuery = {}): AuditEvent[] {\n const path = getAuditPath();\n if (!existsSync(path)) return [];\n\n try {\n const lines = readFileSync(path, \"utf8\")\n .split(\"\\n\")\n .filter((l) => l.trim());\n\n let events: AuditEvent[] = lines\n .map((line) => {\n try {\n return JSON.parse(line) as AuditEvent;\n } catch {\n return null;\n }\n })\n .filter((e): e is AuditEvent => e !== null);\n\n if (query.key) {\n events = events.filter((e) => e.key === query.key);\n }\n if (query.action) {\n events = events.filter((e) => e.action === query.action);\n }\n if (query.since) {\n const since = new Date(query.since).getTime();\n events = events.filter(\n (e) => new Date(e.timestamp).getTime() >= since,\n );\n }\n\n events.sort(\n (a, b) =>\n new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),\n );\n\n if (query.limit) {\n events = events.slice(0, query.limit);\n }\n\n return events;\n } catch {\n return [];\n }\n}\n\nexport interface AccessAnomaly {\n type: \"burst\" | \"unusual-hour\" | \"new-source\";\n description: string;\n events: AuditEvent[];\n}\n\n/**\n * Detect anomalous access patterns in the audit log.\n */\nexport function detectAnomalies(key?: string): AccessAnomaly[] {\n const recent = queryAudit({\n key,\n action: \"read\",\n since: new Date(Date.now() - 3600000).toISOString(), // last hour\n });\n\n const anomalies: AccessAnomaly[] = [];\n\n // Burst detection: more than 50 reads of the same key in an hour\n if (key && recent.length > 50) {\n anomalies.push({\n type: \"burst\",\n description: `${recent.length} reads of \"${key}\" in the last hour`,\n events: recent.slice(0, 10),\n });\n }\n\n // Unusual hour detection: access between 1am-5am local time\n const nightAccess = recent.filter((e) => {\n const hour = new Date(e.timestamp).getHours();\n return hour >= 1 && hour < 5;\n });\n\n if (nightAccess.length > 0) {\n anomalies.push({\n type: \"unusual-hour\",\n description: `${nightAccess.length} access(es) during unusual hours (1am-5am)`,\n events: nightAccess,\n });\n }\n\n return anomalies;\n}\n","/**\n * Quantum Entanglement: link secrets across projects.\n * When one entangled secret is rotated, all linked copies update.\n *\n * Entanglement is stored as metadata in the envelope. The entanglement\n * registry at ~/.config/q-ring/entanglement.json provides a reverse\n * lookup: given a secret, find all its entangled partners.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nexport interface EntanglementPair {\n /** Source: service/key */\n source: { service: string; key: string };\n /** Target: service/key */\n target: { service: string; key: string };\n /** When this entanglement was created */\n createdAt: string;\n}\n\ninterface EntanglementRegistry {\n pairs: EntanglementPair[];\n}\n\nfunction getRegistryPath(): string {\n const dir = join(homedir(), \".config\", \"q-ring\");\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n return join(dir, \"entanglement.json\");\n}\n\nfunction loadRegistry(): EntanglementRegistry {\n const path = getRegistryPath();\n if (!existsSync(path)) {\n return { pairs: [] };\n }\n try {\n return JSON.parse(readFileSync(path, \"utf8\"));\n } catch {\n return { pairs: [] };\n }\n}\n\nfunction saveRegistry(registry: EntanglementRegistry): void {\n writeFileSync(getRegistryPath(), JSON.stringify(registry, null, 2));\n}\n\nexport function entangle(\n source: { service: string; key: string },\n target: { service: string; key: string },\n): void {\n const registry = loadRegistry();\n\n const exists = registry.pairs.some(\n (p) =>\n p.source.service === source.service &&\n p.source.key === source.key &&\n p.target.service === target.service &&\n p.target.key === target.key,\n );\n\n if (!exists) {\n registry.pairs.push({\n source,\n target,\n createdAt: new Date().toISOString(),\n });\n // Bidirectional: add reverse link too\n registry.pairs.push({\n source: target,\n target: source,\n createdAt: new Date().toISOString(),\n });\n saveRegistry(registry);\n }\n}\n\nexport function disentangle(\n source: { service: string; key: string },\n target: { service: string; key: string },\n): void {\n const registry = loadRegistry();\n registry.pairs = registry.pairs.filter(\n (p) =>\n !(\n (p.source.service === source.service &&\n p.source.key === source.key &&\n p.target.service === target.service &&\n p.target.key === target.key) ||\n (p.source.service === target.service &&\n p.source.key === target.key &&\n p.target.service === source.service &&\n p.target.key === source.key)\n ),\n );\n saveRegistry(registry);\n}\n\n/**\n * Find all entangled partners for a given secret.\n */\nexport function findEntangled(\n source: { service: string; key: string },\n): { service: string; key: string }[] {\n const registry = loadRegistry();\n return registry.pairs\n .filter(\n (p) =>\n p.source.service === source.service && p.source.key === source.key,\n )\n .map((p) => p.target);\n}\n\n/**\n * List all entanglement pairs.\n */\nexport function listEntanglements(): EntanglementPair[] {\n return loadRegistry().pairs;\n}\n","import { Entry, findCredentials } from \"@napi-rs/keyring\";\nimport {\n resolveScope,\n globalService,\n projectService,\n type Scope,\n} from \"./scope.js\";\nimport {\n type QuantumEnvelope,\n type Environment,\n createEnvelope,\n parseEnvelope,\n wrapLegacy,\n serializeEnvelope,\n collapseValue,\n checkDecay,\n recordAccess,\n type DecayStatus,\n type EntanglementLink,\n} from \"./envelope.js\";\nimport { collapseEnvironment, type CollapseContext } from \"./collapse.js\";\nimport { logAudit, type AuditAction } from \"./observer.js\";\nimport { findEntangled, entangle as entangleLink, disentangle as disentangleLink } from \"./entanglement.js\";\nimport { fireHooks } from \"./hooks.js\";\n\nexport interface SecretEntry {\n key: string;\n scope: Scope;\n value?: string;\n envelope?: QuantumEnvelope;\n decay?: DecayStatus;\n}\n\nexport interface KeyringOptions {\n scope?: Scope;\n projectPath?: string;\n /** Environment for superposition collapse */\n env?: Environment;\n /** Audit source */\n source?: \"cli\" | \"mcp\" | \"agent\" | \"api\";\n /** Skip audit logging (for internal polling like the dashboard) */\n silent?: boolean;\n}\n\nexport interface SetSecretOptions extends KeyringOptions {\n /** Environment states for superposition */\n states?: Record<Environment, string>;\n /** Default environment */\n defaultEnv?: Environment;\n /** TTL in seconds (quantum decay) */\n ttlSeconds?: number;\n /** Expiry timestamp */\n expiresAt?: string;\n /** Description */\n description?: string;\n /** Tags */\n tags?: string[];\n /** Format for auto-rotation (e.g. \"api-key\", \"password\", \"uuid\") */\n rotationFormat?: string;\n /** Prefix for auto-rotation (e.g. \"sk-\") */\n rotationPrefix?: string;\n /** Provider for liveness validation (e.g. \"openai\", \"stripe\") */\n provider?: string;\n}\n\nfunction readEnvelope(service: string, key: string): QuantumEnvelope | null {\n const entry = new Entry(service, key);\n const raw = entry.getPassword();\n if (raw === null) return null;\n\n const envelope = parseEnvelope(raw);\n return envelope ?? wrapLegacy(raw);\n}\n\nfunction writeEnvelope(\n service: string,\n key: string,\n envelope: QuantumEnvelope,\n): void {\n const entry = new Entry(service, key);\n entry.setPassword(serializeEnvelope(envelope));\n}\n\nfunction resolveEnv(opts: KeyringOptions): Environment | undefined {\n if (opts.env) return opts.env;\n const result = collapseEnvironment({ projectPath: opts.projectPath });\n return result?.env;\n}\n\n/**\n * Retrieve a secret by key, with scope resolution and superposition collapse.\n * Records the access in the audit log (observer effect).\n */\nexport function getSecret(\n key: string,\n opts: KeyringOptions = {},\n): string | null {\n const scopes = resolveScope(opts);\n const env = resolveEnv(opts);\n const source = opts.source ?? \"cli\";\n\n for (const { service, scope } of scopes) {\n const envelope = readEnvelope(service, key);\n if (!envelope) continue;\n\n // Check decay\n const decay = checkDecay(envelope);\n if (decay.isExpired) {\n logAudit({\n action: \"read\",\n key,\n scope,\n source,\n detail: \"blocked: secret expired (quantum decay)\",\n });\n continue;\n }\n\n // Collapse superposition\n const value = collapseValue(envelope, env);\n if (value === null) continue;\n\n // Observer effect: record access and persist\n const updated = recordAccess(envelope);\n writeEnvelope(service, key, updated);\n\n logAudit({ action: \"read\", key, scope, env, source });\n\n return value;\n }\n\n return null;\n}\n\n/**\n * Get the full envelope for a secret (for inspection, no value extraction).\n */\nexport function getEnvelope(\n key: string,\n opts: KeyringOptions = {},\n): { envelope: QuantumEnvelope; scope: Scope } | null {\n const scopes = resolveScope(opts);\n\n for (const { service, scope } of scopes) {\n const envelope = readEnvelope(service, key);\n if (envelope) return { envelope, scope };\n }\n\n return null;\n}\n\n/**\n * Store a secret with quantum metadata.\n */\nexport function setSecret(\n key: string,\n value: string,\n opts: SetSecretOptions = {},\n): void {\n const scope = opts.scope ?? \"global\";\n const scopes = resolveScope({ ...opts, scope });\n const { service } = scopes[0];\n const source = opts.source ?? \"cli\";\n\n // Check if there's an existing envelope to preserve metadata\n const existing = readEnvelope(service, key);\n\n let envelope: QuantumEnvelope;\n\n const rotFmt = opts.rotationFormat ?? existing?.meta.rotationFormat;\n const rotPfx = opts.rotationPrefix ?? existing?.meta.rotationPrefix;\n const prov = opts.provider ?? existing?.meta.provider;\n\n if (opts.states) {\n envelope = createEnvelope(\"\", {\n states: opts.states,\n defaultEnv: opts.defaultEnv,\n description: opts.description,\n tags: opts.tags,\n ttlSeconds: opts.ttlSeconds,\n expiresAt: opts.expiresAt,\n entangled: existing?.meta.entangled,\n rotationFormat: rotFmt,\n rotationPrefix: rotPfx,\n provider: prov,\n });\n } else {\n envelope = createEnvelope(value, {\n description: opts.description,\n tags: opts.tags,\n ttlSeconds: opts.ttlSeconds,\n expiresAt: opts.expiresAt,\n entangled: existing?.meta.entangled,\n rotationFormat: rotFmt,\n rotationPrefix: rotPfx,\n provider: prov,\n });\n }\n\n // Preserve access count from existing\n if (existing) {\n envelope.meta.createdAt = existing.meta.createdAt;\n envelope.meta.accessCount = existing.meta.accessCount;\n }\n\n writeEnvelope(service, key, envelope);\n logAudit({ action: \"write\", key, scope, source });\n\n // Propagate to entangled secrets\n const entangled = findEntangled({ service, key });\n for (const target of entangled) {\n try {\n const targetEnvelope = readEnvelope(target.service, target.key);\n if (targetEnvelope) {\n if (opts.states) {\n targetEnvelope.states = opts.states;\n } else {\n targetEnvelope.value = value;\n }\n targetEnvelope.meta.updatedAt = new Date().toISOString();\n writeEnvelope(target.service, target.key, targetEnvelope);\n logAudit({\n action: \"entangle\",\n key: target.key,\n scope: \"global\",\n source,\n detail: `propagated from ${key}`,\n });\n }\n } catch {\n // entangled target may not exist\n }\n }\n\n fireHooks({\n action: \"write\",\n key,\n scope,\n timestamp: new Date().toISOString(),\n source,\n }, envelope.meta.tags).catch(() => {});\n}\n\n/**\n * Delete a secret from the specified scope (or both if unscoped).\n */\nexport function deleteSecret(\n key: string,\n opts: KeyringOptions = {},\n): boolean {\n const scopes = resolveScope(opts);\n const source = opts.source ?? \"cli\";\n let deleted = false;\n\n for (const { service, scope } of scopes) {\n const entry = new Entry(service, key);\n try {\n if (entry.deleteCredential()) {\n deleted = true;\n logAudit({ action: \"delete\", key, scope, source });\n fireHooks({\n action: \"delete\",\n key,\n scope,\n timestamp: new Date().toISOString(),\n source,\n }).catch(() => {});\n }\n } catch {\n // not found\n }\n }\n\n return deleted;\n}\n\n/**\n * Check whether a secret exists in any applicable scope.\n */\nexport function hasSecret(\n key: string,\n opts: KeyringOptions = {},\n): boolean {\n const scopes = resolveScope(opts);\n\n for (const { service } of scopes) {\n const envelope = readEnvelope(service, key);\n if (envelope) {\n const decay = checkDecay(envelope);\n if (!decay.isExpired) return true;\n }\n }\n\n return false;\n}\n\n/**\n * List all secrets across applicable scopes with quantum metadata.\n */\nexport function listSecrets(opts: KeyringOptions = {}): SecretEntry[] {\n const source = opts.source ?? \"cli\";\n const services: { service: string; scope: Scope }[] = [];\n\n if (!opts.scope || opts.scope === \"global\") {\n services.push({ service: globalService(), scope: \"global\" });\n }\n\n if ((!opts.scope || opts.scope === \"project\") && opts.projectPath) {\n services.push({\n service: projectService(opts.projectPath),\n scope: \"project\",\n });\n }\n\n const results: SecretEntry[] = [];\n const seen = new Set<string>();\n\n for (const { service, scope } of services) {\n try {\n const credentials = findCredentials(service);\n for (const cred of credentials) {\n const id = `${scope}:${cred.account}`;\n if (seen.has(id)) continue;\n seen.add(id);\n\n const envelope = parseEnvelope(cred.password) ?? wrapLegacy(cred.password);\n const decay = checkDecay(envelope);\n\n results.push({\n key: cred.account,\n scope,\n envelope,\n decay,\n });\n }\n } catch {\n // keyring unavailable\n }\n }\n\n if (!opts.silent) {\n logAudit({ action: \"list\", source });\n }\n\n return results.sort((a, b) => a.key.localeCompare(b.key));\n}\n\n/**\n * Export all secrets with their values (for .env or JSON export).\n * Collapses superposition based on detected environment.\n */\nexport function exportSecrets(\n opts: KeyringOptions & { format?: \"env\" | \"json\"; keys?: string[]; tags?: string[] } = {},\n): string {\n const format = opts.format ?? \"env\";\n const env = resolveEnv(opts);\n let entries = listSecrets(opts);\n const source = opts.source ?? \"cli\";\n\n if (opts.keys?.length) {\n const keySet = new Set(opts.keys);\n entries = entries.filter((e) => keySet.has(e.key));\n }\n\n if (opts.tags?.length) {\n entries = entries.filter((e) =>\n opts.tags!.some((t) => e.envelope?.meta.tags?.includes(t)),\n );\n }\n\n const merged = new Map<string, string>();\n\n const globalEntries = entries.filter((e) => e.scope === \"global\");\n const projectEntries = entries.filter((e) => e.scope === \"project\");\n\n for (const entry of [...globalEntries, ...projectEntries]) {\n if (entry.envelope) {\n const decay = checkDecay(entry.envelope);\n if (decay.isExpired) continue;\n\n const value = collapseValue(entry.envelope, env);\n if (value !== null) {\n merged.set(entry.key, value);\n }\n }\n }\n\n logAudit({ action: \"export\", source, detail: `format=${format}` });\n\n if (format === \"json\") {\n const obj: Record<string, string> = {};\n for (const [key, value] of merged) {\n obj[key] = value;\n }\n return JSON.stringify(obj, null, 2);\n }\n\n const lines: string[] = [];\n for (const [key, value] of merged) {\n const escaped = value\n .replace(/\\\\/g, \"\\\\\\\\\")\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, \"\\\\n\");\n lines.push(`${key}=\"${escaped}\"`);\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * Create an entanglement between two secrets.\n */\nexport function entangleSecrets(\n sourceKey: string,\n sourceOpts: KeyringOptions,\n targetKey: string,\n targetOpts: KeyringOptions,\n): void {\n const sourceScopes = resolveScope({ ...sourceOpts, scope: sourceOpts.scope ?? \"global\" });\n const targetScopes = resolveScope({ ...targetOpts, scope: targetOpts.scope ?? \"global\" });\n\n const source = { service: sourceScopes[0].service, key: sourceKey };\n const target = { service: targetScopes[0].service, key: targetKey };\n\n entangleLink(source, target);\n logAudit({\n action: \"entangle\",\n key: sourceKey,\n source: sourceOpts.source ?? \"cli\",\n detail: `entangled with ${targetKey}`,\n });\n}\n\n/**\n * Remove an entanglement between two secrets.\n */\nexport function disentangleSecrets(\n sourceKey: string,\n sourceOpts: KeyringOptions,\n targetKey: string,\n targetOpts: KeyringOptions,\n): void {\n const sourceScopes = resolveScope({ ...sourceOpts, scope: sourceOpts.scope ?? \"global\" });\n const targetScopes = resolveScope({ ...targetOpts, scope: targetOpts.scope ?? \"global\" });\n\n const source = { service: sourceScopes[0].service, key: sourceKey };\n const target = { service: targetScopes[0].service, key: targetKey };\n\n disentangleLink(source, target);\n logAudit({\n action: \"entangle\",\n key: sourceKey,\n source: sourceOpts.source ?? \"cli\",\n detail: `disentangled from ${targetKey}`,\n });\n}\n","import { createHash } from \"node:crypto\";\n\nexport function hashProjectPath(projectPath: string): string {\n return createHash(\"sha256\").update(projectPath).digest(\"hex\").slice(0, 12);\n}\n","import { hashProjectPath } from \"../utils/hash.js\";\n\nconst SERVICE_PREFIX = \"q-ring\";\n\nexport type Scope = \"global\" | \"project\";\n\nexport interface ResolvedScope {\n scope: Scope;\n service: string;\n projectPath?: string;\n}\n\nexport function globalService(): string {\n return `${SERVICE_PREFIX}:global`;\n}\n\nexport function projectService(projectPath: string): string {\n const hash = hashProjectPath(projectPath);\n return `${SERVICE_PREFIX}:project:${hash}`;\n}\n\nexport function resolveScope(opts: {\n scope?: Scope;\n projectPath?: string;\n}): ResolvedScope[] {\n const { scope, projectPath } = opts;\n\n if (scope === \"global\") {\n return [{ scope: \"global\", service: globalService() }];\n }\n\n if (scope === \"project\") {\n if (!projectPath) {\n throw new Error(\"Project path is required for project scope\");\n }\n return [\n { scope: \"project\", service: projectService(projectPath), projectPath },\n ];\n }\n\n // No explicit scope: return project-first for resolution (project overrides global)\n if (projectPath) {\n return [\n { scope: \"project\", service: projectService(projectPath), projectPath },\n { scope: \"global\", service: globalService() },\n ];\n }\n\n return [{ scope: \"global\", service: globalService() }];\n}\n\nexport function parseServiceName(service: string): ResolvedScope {\n if (service === globalService()) {\n return { scope: \"global\", service };\n }\n\n if (service.startsWith(`${SERVICE_PREFIX}:project:`)) {\n return { scope: \"project\", service };\n }\n\n return { scope: \"global\", service };\n}\n","/**\n * Hook system: fire callbacks when secrets are created, updated, or deleted.\n * Supports shell commands, HTTP webhooks, and process signals.\n *\n * Registry stored at ~/.config/q-ring/hooks.json\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { exec } from \"node:child_process\";\nimport { request as httpsRequest } from \"node:https\";\nimport { request as httpRequest } from \"node:http\";\nimport { randomUUID } from \"node:crypto\";\nimport { logAudit } from \"./observer.js\";\n\nexport type HookType = \"shell\" | \"http\" | \"signal\";\nexport type HookAction = \"write\" | \"delete\" | \"rotate\";\n\nexport interface HookMatch {\n key?: string;\n keyPattern?: string;\n tag?: string;\n scope?: \"global\" | \"project\";\n action?: HookAction[];\n}\n\nexport interface HookEntry {\n id: string;\n type: HookType;\n match: HookMatch;\n command?: string;\n url?: string;\n signal?: { target: string; signal?: string };\n description?: string;\n createdAt: string;\n enabled: boolean;\n}\n\nexport interface HookPayload {\n action: HookAction;\n key: string;\n scope: string;\n timestamp: string;\n source: \"cli\" | \"mcp\" | \"agent\" | \"api\";\n}\n\nexport interface HookResult {\n hookId: string;\n success: boolean;\n message: string;\n}\n\ninterface HookRegistry {\n hooks: HookEntry[];\n}\n\nfunction getRegistryPath(): string {\n const dir = join(homedir(), \".config\", \"q-ring\");\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n return join(dir, \"hooks.json\");\n}\n\nfunction loadRegistry(): HookRegistry {\n const path = getRegistryPath();\n if (!existsSync(path)) {\n return { hooks: [] };\n }\n try {\n return JSON.parse(readFileSync(path, \"utf8\"));\n } catch {\n return { hooks: [] };\n }\n}\n\nfunction saveRegistry(registry: HookRegistry): void {\n writeFileSync(getRegistryPath(), JSON.stringify(registry, null, 2));\n}\n\nexport function registerHook(\n entry: Omit<HookEntry, \"id\" | \"createdAt\">,\n): HookEntry {\n const registry = loadRegistry();\n const hook: HookEntry = {\n ...entry,\n id: randomUUID().slice(0, 8),\n createdAt: new Date().toISOString(),\n };\n registry.hooks.push(hook);\n saveRegistry(registry);\n return hook;\n}\n\nexport function removeHook(id: string): boolean {\n const registry = loadRegistry();\n const before = registry.hooks.length;\n registry.hooks = registry.hooks.filter((h) => h.id !== id);\n if (registry.hooks.length < before) {\n saveRegistry(registry);\n return true;\n }\n return false;\n}\n\nexport function listHooks(): HookEntry[] {\n return loadRegistry().hooks;\n}\n\nexport function enableHook(id: string): boolean {\n const registry = loadRegistry();\n const hook = registry.hooks.find((h) => h.id === id);\n if (!hook) return false;\n hook.enabled = true;\n saveRegistry(registry);\n return true;\n}\n\nexport function disableHook(id: string): boolean {\n const registry = loadRegistry();\n const hook = registry.hooks.find((h) => h.id === id);\n if (!hook) return false;\n hook.enabled = false;\n saveRegistry(registry);\n return true;\n}\n\nfunction matchesHook(\n hook: HookEntry,\n payload: HookPayload,\n tags?: string[],\n): boolean {\n if (!hook.enabled) return false;\n\n const m = hook.match;\n\n if (m.action?.length && !m.action.includes(payload.action)) return false;\n\n if (m.key && m.key !== payload.key) return false;\n\n if (m.keyPattern) {\n const regex = new RegExp(\n \"^\" + m.keyPattern.replace(/\\*/g, \".*\") + \"$\",\n \"i\",\n );\n if (!regex.test(payload.key)) return false;\n }\n\n if (m.tag && (!tags || !tags.includes(m.tag))) return false;\n\n if (m.scope && m.scope !== payload.scope) return false;\n\n return true;\n}\n\nfunction executeShell(command: string, payload: HookPayload): Promise<HookResult> {\n return new Promise((resolve) => {\n const env = {\n ...process.env,\n QRING_HOOK_KEY: payload.key,\n QRING_HOOK_ACTION: payload.action,\n QRING_HOOK_SCOPE: payload.scope,\n };\n\n exec(command, { timeout: 30000, env }, (err, stdout, stderr) => {\n if (err) {\n resolve({ hookId: \"\", success: false, message: `Shell error: ${err.message}` });\n } else {\n resolve({ hookId: \"\", success: true, message: stdout.trim() || \"OK\" });\n }\n });\n });\n}\n\nfunction executeHttp(url: string, payload: HookPayload): Promise<HookResult> {\n return new Promise((resolve) => {\n const body = JSON.stringify(payload);\n const parsedUrl = new URL(url);\n const reqFn = parsedUrl.protocol === \"https:\" ? httpsRequest : httpRequest;\n\n const req = reqFn(\n url,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(body),\n \"User-Agent\": \"q-ring-hooks/1.0\",\n },\n timeout: 10000,\n },\n (res) => {\n let data = \"\";\n res.on(\"data\", (chunk) => (data += chunk));\n res.on(\"end\", () => {\n resolve({\n hookId: \"\",\n success: (res.statusCode ?? 0) >= 200 && (res.statusCode ?? 0) < 300,\n message: `HTTP ${res.statusCode}`,\n });\n });\n },\n );\n\n req.on(\"error\", (err) => {\n resolve({ hookId: \"\", success: false, message: `HTTP error: ${err.message}` });\n });\n req.on(\"timeout\", () => {\n req.destroy();\n resolve({ hookId: \"\", success: false, message: \"HTTP timeout\" });\n });\n req.write(body);\n req.end();\n });\n}\n\nfunction executeSignal(\n target: string,\n signal: string = \"SIGHUP\",\n): Promise<HookResult> {\n return new Promise((resolve) => {\n const pid = parseInt(target, 10);\n if (!isNaN(pid)) {\n try {\n process.kill(pid, signal as NodeJS.Signals);\n resolve({ hookId: \"\", success: true, message: `Signal ${signal} sent to PID ${pid}` });\n } catch (err) {\n resolve({ hookId: \"\", success: false, message: `Signal error: ${err instanceof Error ? err.message : String(err)}` });\n }\n return;\n }\n\n exec(`pgrep -f \"${target}\"`, { timeout: 5000 }, (err, stdout) => {\n if (err || !stdout.trim()) {\n resolve({ hookId: \"\", success: false, message: `Process \"${target}\" not found` });\n return;\n }\n const pids = stdout.trim().split(\"\\n\").map((p) => parseInt(p.trim(), 10)).filter((p) => !isNaN(p));\n let sent = 0;\n for (const p of pids) {\n try {\n process.kill(p, signal as NodeJS.Signals);\n sent++;\n } catch { /* ignore dead PIDs */ }\n }\n resolve({ hookId: \"\", success: sent > 0, message: `Signal ${signal} sent to ${sent} process(es)` });\n });\n });\n}\n\nasync function executeHook(\n hook: HookEntry,\n payload: HookPayload,\n): Promise<HookResult> {\n let result: HookResult;\n\n switch (hook.type) {\n case \"shell\":\n result = hook.command\n ? await executeShell(hook.command, payload)\n : { hookId: hook.id, success: false, message: \"No command specified\" };\n break;\n case \"http\":\n result = hook.url\n ? await executeHttp(hook.url, payload)\n : { hookId: hook.id, success: false, message: \"No URL specified\" };\n break;\n case \"signal\":\n result = hook.signal\n ? await executeSignal(hook.signal.target, hook.signal.signal)\n : { hookId: hook.id, success: false, message: \"No signal target specified\" };\n break;\n default:\n result = { hookId: hook.id, success: false, message: `Unknown hook type: ${hook.type}` };\n }\n\n result.hookId = hook.id;\n return result;\n}\n\n/**\n * Fire all matching hooks for a given payload. Fire-and-forget — never blocks\n * the caller on hook failures.\n */\nexport async function fireHooks(\n payload: HookPayload,\n tags?: string[],\n): Promise<HookResult[]> {\n const hooks = listHooks();\n const matching = hooks.filter((h) => matchesHook(h, payload, tags));\n\n if (matching.length === 0) return [];\n\n const results = await Promise.allSettled(\n matching.map((h) => executeHook(h, payload)),\n );\n\n const hookResults: HookResult[] = [];\n for (const r of results) {\n if (r.status === \"fulfilled\") {\n hookResults.push(r.value);\n } else {\n hookResults.push({\n hookId: \"unknown\",\n success: false,\n message: r.reason?.message ?? \"Hook execution failed\",\n });\n }\n }\n\n for (const r of hookResults) {\n try {\n logAudit({\n action: \"write\",\n key: payload.key,\n scope: payload.scope,\n source: payload.source,\n detail: `hook:${r.hookId} ${r.success ? \"ok\" : \"fail\"} — ${r.message}`,\n });\n } catch { /* never crash on audit logging */ }\n }\n\n return hookResults;\n}\n","/**\n * Quantum Tunneling: ephemeral secrets that exist only in memory.\n *\n * Tunneled secrets are never persisted to the OS keyring. They live\n * in a process-scoped in-memory store with optional auto-expiry.\n * Useful for passing secrets between agents without touching disk.\n */\n\ninterface TunnelEntry {\n value: string;\n createdAt: number;\n expiresAt?: number;\n accessCount: number;\n /** Max number of reads before auto-destruct */\n maxReads?: number;\n}\n\nconst tunnelStore = new Map<string, TunnelEntry>();\n\nlet cleanupInterval: ReturnType<typeof setInterval> | null = null;\n\nfunction ensureCleanup(): void {\n if (cleanupInterval) return;\n cleanupInterval = setInterval(() => {\n const now = Date.now();\n for (const [id, entry] of tunnelStore) {\n if (entry.expiresAt && now >= entry.expiresAt) {\n tunnelStore.delete(id);\n }\n }\n if (tunnelStore.size === 0 && cleanupInterval) {\n clearInterval(cleanupInterval);\n cleanupInterval = null;\n }\n }, 5000);\n\n // Don't prevent process exit\n if (cleanupInterval && typeof cleanupInterval === \"object\" && \"unref\" in cleanupInterval) {\n cleanupInterval.unref();\n }\n}\n\nexport interface TunnelOptions {\n /** TTL in seconds */\n ttlSeconds?: number;\n /** Self-destruct after N reads */\n maxReads?: number;\n}\n\n/**\n * Create a tunneled (ephemeral) secret. Returns a tunnel ID.\n */\nexport function tunnelCreate(\n value: string,\n opts: TunnelOptions = {},\n): string {\n const id = `tun_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;\n const now = Date.now();\n\n tunnelStore.set(id, {\n value,\n createdAt: now,\n expiresAt: opts.ttlSeconds ? now + opts.ttlSeconds * 1000 : undefined,\n accessCount: 0,\n maxReads: opts.maxReads,\n });\n\n ensureCleanup();\n return id;\n}\n\n/**\n * Read a tunneled secret by ID. Returns null if expired or not found.\n * Each read increments the access counter; auto-destructs after maxReads.\n */\nexport function tunnelRead(id: string): string | null {\n const entry = tunnelStore.get(id);\n if (!entry) return null;\n\n if (entry.expiresAt && Date.now() >= entry.expiresAt) {\n tunnelStore.delete(id);\n return null;\n }\n\n entry.accessCount++;\n\n if (entry.maxReads && entry.accessCount >= entry.maxReads) {\n const value = entry.value;\n tunnelStore.delete(id);\n return value;\n }\n\n return entry.value;\n}\n\n/**\n * Destroy a tunneled secret immediately.\n */\nexport function tunnelDestroy(id: string): boolean {\n return tunnelStore.delete(id);\n}\n\n/**\n * List all active tunnel IDs (never exposes values).\n */\nexport function tunnelList(): {\n id: string;\n createdAt: number;\n expiresAt?: number;\n accessCount: number;\n maxReads?: number;\n}[] {\n const now = Date.now();\n const result: ReturnType<typeof tunnelList> = [];\n\n for (const [id, entry] of tunnelStore) {\n if (entry.expiresAt && now >= entry.expiresAt) {\n tunnelStore.delete(id);\n continue;\n }\n result.push({\n id,\n createdAt: entry.createdAt,\n expiresAt: entry.expiresAt,\n accessCount: entry.accessCount,\n maxReads: entry.maxReads,\n });\n }\n\n return result;\n}\n"],"mappings":";;;AAyDO,SAAS,eACd,OACA,MAUiB;AACjB,QAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AAEnC,MAAI,YAAY,MAAM;AACtB,MAAI,CAAC,aAAa,MAAM,YAAY;AAClC,gBAAY,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,aAAa,GAAI,EAAE,YAAY;AAAA,EACxE;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,OAAO,MAAM,SAAS,SAAY;AAAA,IAClC,QAAQ,MAAM;AAAA,IACd,YAAY,MAAM;AAAA,IAClB,MAAM;AAAA,MACJ,WAAW;AAAA,MACX,WAAW;AAAA,MACX;AAAA,MACA,YAAY,MAAM;AAAA,MAClB,aAAa,MAAM;AAAA,MACnB,MAAM,MAAM;AAAA,MACZ,WAAW,MAAM;AAAA,MACjB,aAAa;AAAA,MACb,gBAAgB,MAAM;AAAA,MACtB,gBAAgB,MAAM;AAAA,MACtB,UAAU,MAAM;AAAA,IAClB;AAAA,EACF;AACF;AAEO,SAAS,cAAc,KAAqC;AACjE,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,UAAU,OAAO,WAAW,YAAY,OAAO,MAAM,GAAG;AAC1D,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAMO,SAAS,WAAW,UAAmC;AAC5D,QAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AACnC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,OAAO;AAAA,IACP,MAAM;AAAA,MACJ,WAAW;AAAA,MACX,WAAW;AAAA,MACX,aAAa;AAAA,IACf;AAAA,EACF;AACF;AAEO,SAAS,kBAAkB,UAAmC;AACnE,SAAO,KAAK,UAAU,QAAQ;AAChC;AAMO,SAAS,cACd,UACA,KACe;AACf,MAAI,SAAS,QAAQ;AACnB,UAAM,YAAY,OAAO,SAAS;AAClC,QAAI,aAAa,SAAS,OAAO,SAAS,GAAG;AAC3C,aAAO,SAAS,OAAO,SAAS;AAAA,IAClC;AAEA,QAAI,SAAS,cAAc,SAAS,OAAO,SAAS,UAAU,GAAG;AAC/D,aAAO,SAAS,OAAO,SAAS,UAAU;AAAA,IAC5C;AAEA,UAAM,OAAO,OAAO,KAAK,SAAS,MAAM;AACxC,QAAI,KAAK,SAAS,GAAG;AACnB,aAAO,SAAS,OAAO,KAAK,CAAC,CAAC;AAAA,IAChC;AACA,WAAO;AAAA,EACT;AAEA,SAAO,SAAS,SAAS;AAC3B;AAaO,SAAS,WAAW,UAAwC;AACjE,MAAI,CAAC,SAAS,KAAK,WAAW;AAC5B,WAAO;AAAA,MACL,WAAW;AAAA,MACX,SAAS;AAAA,MACT,iBAAiB;AAAA,MACjB,kBAAkB;AAAA,MAClB,eAAe;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,UAAU,IAAI,KAAK,SAAS,KAAK,SAAS,EAAE,QAAQ;AAC1D,QAAM,UAAU,IAAI,KAAK,SAAS,KAAK,SAAS,EAAE,QAAQ;AAC1D,QAAM,gBAAgB,UAAU;AAChC,QAAM,UAAU,MAAM;AACtB,QAAM,YAAY,UAAU;AAE5B,QAAM,kBACJ,gBAAgB,IAAI,KAAK,MAAO,UAAU,gBAAiB,GAAG,IAAI;AAEpE,QAAM,mBAAmB,KAAK,MAAM,YAAY,GAAI;AAEpD,MAAI,gBAA+B;AACnC,MAAI,YAAY,GAAG;AACjB,UAAM,OAAO,KAAK,MAAM,YAAY,KAAQ;AAC5C,UAAM,QAAQ,KAAK,MAAO,YAAY,QAAY,IAAO;AACzD,UAAM,UAAU,KAAK,MAAO,YAAY,OAAW,GAAK;AAExD,QAAI,OAAO,EAAG,iBAAgB,GAAG,IAAI,KAAK,KAAK;AAAA,aACtC,QAAQ,EAAG,iBAAgB,GAAG,KAAK,KAAK,OAAO;AAAA,QACnD,iBAAgB,GAAG,OAAO;AAAA,EACjC,OAAO;AACL,oBAAgB;AAAA,EAClB;AAEA,SAAO;AAAA,IACL,WAAW,aAAa;AAAA,IACxB,SAAS,mBAAmB;AAAA,IAC5B;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAMO,SAAS,aAAa,UAA4C;AACvE,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM;AAAA,MACJ,GAAG,SAAS;AAAA,MACZ,aAAa,SAAS,KAAK,cAAc;AAAA,MACzC,iBAAgB,oBAAI,KAAK,GAAE,YAAY;AAAA,IACzC;AAAA,EACF;AACF;;;ACxNA,SAAS,gBAAgB;AACzB,SAAS,YAAY,oBAAoB;AACzC,SAAS,YAAY;AAGrB,IAAM,iBAA8C;AAAA,EAClD,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,aAAa;AAAA,EACb,KAAK;AAAA,EACL,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS;AACX;AAEA,SAAS,gBAAgB,KAA6B;AACpD,MAAI;AACF,UAAM,SAAS,SAAS,mCAAmC;AAAA,MACzD,KAAK,OAAO,QAAQ,IAAI;AAAA,MACxB,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,MAC9B,UAAU;AAAA,MACV,SAAS;AAAA,IACX,CAAC,EAAE,KAAK;AACR,WAAO,UAAU;AAAA,EACnB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAuBO,SAAS,kBAAkB,aAA4C;AAC5E,QAAM,aAAa,KAAK,eAAe,QAAQ,IAAI,GAAG,cAAc;AACpE,MAAI;AACF,QAAI,WAAW,UAAU,GAAG;AAC1B,aAAO,KAAK,MAAM,aAAa,YAAY,MAAM,CAAC;AAAA,IACpD;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAoBO,SAAS,oBACd,MAAuB,CAAC,GACD;AACvB,MAAI,IAAI,UAAU;AAChB,WAAO,EAAE,KAAK,IAAI,UAAU,QAAQ,WAAW;AAAA,EACjD;AAEA,QAAM,WAAW,QAAQ,IAAI;AAC7B,MAAI,UAAU;AACZ,WAAO,EAAE,KAAK,UAAU,QAAQ,YAAY;AAAA,EAC9C;AAEA,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,SAAS;AACX,UAAM,SAAS,WAAW,OAAO;AACjC,WAAO,EAAE,KAAK,QAAQ,QAAQ,WAAW;AAAA,EAC3C;AAEA,QAAM,SAAS,kBAAkB,IAAI,WAAW;AAChD,MAAI,QAAQ,KAAK;AACf,WAAO,EAAE,KAAK,OAAO,KAAK,QAAQ,iBAAiB;AAAA,EACrD;AAEA,QAAM,SAAS,gBAAgB,IAAI,WAAW;AAC9C,MAAI,QAAQ;AACV,UAAM,YAAY,EAAE,GAAG,gBAAgB,GAAG,QAAQ,UAAU;AAC5D,UAAM,SAAS,UAAU,MAAM,KAAK,UAAU,WAAW,MAAM;AAC/D,QAAI,QAAQ;AACV,aAAO,EAAE,KAAK,QAAQ,QAAQ,aAAa;AAAA,IAC7C;AAAA,EACF;AAEA,MAAI,QAAQ,YAAY;AACtB,WAAO,EAAE,KAAK,OAAO,YAAY,QAAQ,iBAAiB;AAAA,EAC5D;AAEA,SAAO;AACT;AAMA,SAAS,UACP,WACA,QACyB;AACzB,aAAW,CAAC,SAAS,GAAG,KAAK,OAAO,QAAQ,SAAS,GAAG;AACtD,QAAI,CAAC,QAAQ,SAAS,GAAG,EAAG;AAC5B,UAAM,QAAQ,IAAI;AAAA,MAChB,MAAM,QAAQ,QAAQ,OAAO,IAAI,IAAI;AAAA,IACvC;AACA,QAAI,MAAM,KAAK,MAAM,EAAG,QAAO;AAAA,EACjC;AACA,SAAO;AACT;AAEA,SAAS,WAAW,KAA0B;AAC5C,QAAM,QAAQ,IAAI,YAAY;AAC9B,MAAI,UAAU,aAAc,QAAO;AACnC,MAAI,UAAU,cAAe,QAAO;AACpC,SAAO;AACT;;;ACrJA,SAAS,cAAAA,aAAY,WAAW,gBAAgB,gBAAAC,qBAAoB;AACpE,SAAS,QAAAC,aAAY;AACrB,SAAS,eAAe;AA2BxB,SAAS,cAAsB;AAC7B,QAAM,MAAMA,MAAK,QAAQ,GAAG,WAAW,QAAQ;AAC/C,MAAI,CAACF,YAAW,GAAG,GAAG;AACpB,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACpC;AACA,SAAO;AACT;AAEA,SAAS,eAAuB;AAC9B,SAAOE,MAAK,YAAY,GAAG,aAAa;AAC1C;AAEO,SAAS,SAAS,OAAoD;AAC3E,QAAM,OAAmB;AAAA,IACvB,GAAG;AAAA,IACH,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,KAAK,QAAQ;AAAA,EACf;AAEA,MAAI;AACF,mBAAe,aAAa,GAAG,KAAK,UAAU,IAAI,IAAI,IAAI;AAAA,EAC5D,QAAQ;AAAA,EAER;AACF;AASO,SAAS,WAAW,QAAoB,CAAC,GAAiB;AAC/D,QAAM,OAAO,aAAa;AAC1B,MAAI,CAACF,YAAW,IAAI,EAAG,QAAO,CAAC;AAE/B,MAAI;AACF,UAAM,QAAQC,cAAa,MAAM,MAAM,EACpC,MAAM,IAAI,EACV,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC;AAEzB,QAAI,SAAuB,MACxB,IAAI,CAAC,SAAS;AACb,UAAI;AACF,eAAO,KAAK,MAAM,IAAI;AAAA,MACxB,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF,CAAC,EACA,OAAO,CAAC,MAAuB,MAAM,IAAI;AAE5C,QAAI,MAAM,KAAK;AACb,eAAS,OAAO,OAAO,CAAC,MAAM,EAAE,QAAQ,MAAM,GAAG;AAAA,IACnD;AACA,QAAI,MAAM,QAAQ;AAChB,eAAS,OAAO,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,MAAM;AAAA,IACzD;AACA,QAAI,MAAM,OAAO;AACf,YAAM,QAAQ,IAAI,KAAK,MAAM,KAAK,EAAE,QAAQ;AAC5C,eAAS,OAAO;AAAA,QACd,CAAC,MAAM,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,KAAK;AAAA,MAC5C;AAAA,IACF;AAEA,WAAO;AAAA,MACL,CAAC,GAAG,MACF,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,IAAI,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ;AAAA,IACpE;AAEA,QAAI,MAAM,OAAO;AACf,eAAS,OAAO,MAAM,GAAG,MAAM,KAAK;AAAA,IACtC;AAEA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAWO,SAAS,gBAAgB,KAA+B;AAC7D,QAAM,SAAS,WAAW;AAAA,IACxB;AAAA,IACA,QAAQ;AAAA,IACR,OAAO,IAAI,KAAK,KAAK,IAAI,IAAI,IAAO,EAAE,YAAY;AAAA;AAAA,EACpD,CAAC;AAED,QAAM,YAA6B,CAAC;AAGpC,MAAI,OAAO,OAAO,SAAS,IAAI;AAC7B,cAAU,KAAK;AAAA,MACb,MAAM;AAAA,MACN,aAAa,GAAG,OAAO,MAAM,cAAc,GAAG;AAAA,MAC9C,QAAQ,OAAO,MAAM,GAAG,EAAE;AAAA,IAC5B,CAAC;AAAA,EACH;AAGA,QAAM,cAAc,OAAO,OAAO,CAAC,MAAM;AACvC,UAAM,OAAO,IAAI,KAAK,EAAE,SAAS,EAAE,SAAS;AAC5C,WAAO,QAAQ,KAAK,OAAO;AAAA,EAC7B,CAAC;AAED,MAAI,YAAY,SAAS,GAAG;AAC1B,cAAU,KAAK;AAAA,MACb,MAAM;AAAA,MACN,aAAa,GAAG,YAAY,MAAM;AAAA,MAClC,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;ACtJA,SAAS,cAAAE,aAAY,gBAAAC,eAAc,eAAe,aAAAC,kBAAiB;AACnE,SAAS,QAAAC,aAAY;AACrB,SAAS,WAAAC,gBAAe;AAexB,SAAS,kBAA0B;AACjC,QAAM,MAAMD,MAAKC,SAAQ,GAAG,WAAW,QAAQ;AAC/C,MAAI,CAACJ,YAAW,GAAG,GAAG;AACpB,IAAAE,WAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACpC;AACA,SAAOC,MAAK,KAAK,mBAAmB;AACtC;AAEA,SAAS,eAAqC;AAC5C,QAAM,OAAO,gBAAgB;AAC7B,MAAI,CAACH,YAAW,IAAI,GAAG;AACrB,WAAO,EAAE,OAAO,CAAC,EAAE;AAAA,EACrB;AACA,MAAI;AACF,WAAO,KAAK,MAAMC,cAAa,MAAM,MAAM,CAAC;AAAA,EAC9C,QAAQ;AACN,WAAO,EAAE,OAAO,CAAC,EAAE;AAAA,EACrB;AACF;AAEA,SAAS,aAAa,UAAsC;AAC1D,gBAAc,gBAAgB,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AACpE;AAEO,SAAS,SACd,QACA,QACM;AACN,QAAM,WAAW,aAAa;AAE9B,QAAM,SAAS,SAAS,MAAM;AAAA,IAC5B,CAAC,MACC,EAAE,OAAO,YAAY,OAAO,WAC5B,EAAE,OAAO,QAAQ,OAAO,OACxB,EAAE,OAAO,YAAY,OAAO,WAC5B,EAAE,OAAO,QAAQ,OAAO;AAAA,EAC5B;AAEA,MAAI,CAAC,QAAQ;AACX,aAAS,MAAM,KAAK;AAAA,MAClB;AAAA,MACA;AAAA,MACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC,CAAC;AAED,aAAS,MAAM,KAAK;AAAA,MAClB,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC,CAAC;AACD,iBAAa,QAAQ;AAAA,EACvB;AACF;AAEO,SAAS,YACd,QACA,QACM;AACN,QAAM,WAAW,aAAa;AAC9B,WAAS,QAAQ,SAAS,MAAM;AAAA,IAC9B,CAAC,MACC,EACG,EAAE,OAAO,YAAY,OAAO,WAC3B,EAAE,OAAO,QAAQ,OAAO,OACxB,EAAE,OAAO,YAAY,OAAO,WAC5B,EAAE,OAAO,QAAQ,OAAO,OACzB,EAAE,OAAO,YAAY,OAAO,WAC3B,EAAE,OAAO,QAAQ,OAAO,OACxB,EAAE,OAAO,YAAY,OAAO,WAC5B,EAAE,OAAO,QAAQ,OAAO;AAAA,EAEhC;AACA,eAAa,QAAQ;AACvB;AAKO,SAAS,cACd,QACoC;AACpC,QAAM,WAAW,aAAa;AAC9B,SAAO,SAAS,MACb;AAAA,IACC,CAAC,MACC,EAAE,OAAO,YAAY,OAAO,WAAW,EAAE,OAAO,QAAQ,OAAO;AAAA,EACnE,EACC,IAAI,CAAC,MAAM,EAAE,MAAM;AACxB;AAKO,SAAS,oBAAwC;AACtD,SAAO,aAAa,EAAE;AACxB;;;ACzHA,SAAS,OAAO,uBAAuB;;;ACAvC,SAAS,kBAAkB;AAEpB,SAAS,gBAAgB,aAA6B;AAC3D,SAAO,WAAW,QAAQ,EAAE,OAAO,WAAW,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AAC3E;;;ACFA,IAAM,iBAAiB;AAUhB,SAAS,gBAAwB;AACtC,SAAO,GAAG,cAAc;AAC1B;AAEO,SAAS,eAAe,aAA6B;AAC1D,QAAM,OAAO,gBAAgB,WAAW;AACxC,SAAO,GAAG,cAAc,YAAY,IAAI;AAC1C;AAEO,SAAS,aAAa,MAGT;AAClB,QAAM,EAAE,OAAO,YAAY,IAAI;AAE/B,MAAI,UAAU,UAAU;AACtB,WAAO,CAAC,EAAE,OAAO,UAAU,SAAS,cAAc,EAAE,CAAC;AAAA,EACvD;AAEA,MAAI,UAAU,WAAW;AACvB,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,MAAM,4CAA4C;AAAA,IAC9D;AACA,WAAO;AAAA,MACL,EAAE,OAAO,WAAW,SAAS,eAAe,WAAW,GAAG,YAAY;AAAA,IACxE;AAAA,EACF;AAGA,MAAI,aAAa;AACf,WAAO;AAAA,MACL,EAAE,OAAO,WAAW,SAAS,eAAe,WAAW,GAAG,YAAY;AAAA,MACtE,EAAE,OAAO,UAAU,SAAS,cAAc,EAAE;AAAA,IAC9C;AAAA,EACF;AAEA,SAAO,CAAC,EAAE,OAAO,UAAU,SAAS,cAAc,EAAE,CAAC;AACvD;;;AC1CA,SAAS,cAAAI,aAAY,gBAAAC,eAAc,iBAAAC,gBAAe,aAAAC,kBAAiB;AACnE,SAAS,QAAAC,aAAY;AACrB,SAAS,WAAAC,gBAAe;AACxB,SAAS,YAAY;AACrB,SAAS,WAAW,oBAAoB;AACxC,SAAS,WAAW,mBAAmB;AACvC,SAAS,kBAAkB;AA4C3B,SAASC,mBAA0B;AACjC,QAAM,MAAMC,MAAKC,SAAQ,GAAG,WAAW,QAAQ;AAC/C,MAAI,CAACC,YAAW,GAAG,GAAG;AACpB,IAAAC,WAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACpC;AACA,SAAOH,MAAK,KAAK,YAAY;AAC/B;AAEA,SAASI,gBAA6B;AACpC,QAAM,OAAOL,iBAAgB;AAC7B,MAAI,CAACG,YAAW,IAAI,GAAG;AACrB,WAAO,EAAE,OAAO,CAAC,EAAE;AAAA,EACrB;AACA,MAAI;AACF,WAAO,KAAK,MAAMG,cAAa,MAAM,MAAM,CAAC;AAAA,EAC9C,QAAQ;AACN,WAAO,EAAE,OAAO,CAAC,EAAE;AAAA,EACrB;AACF;AAEA,SAASC,cAAa,UAA8B;AAClD,EAAAC,eAAcR,iBAAgB,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AACpE;AAEO,SAAS,aACd,OACW;AACX,QAAM,WAAWK,cAAa;AAC9B,QAAM,OAAkB;AAAA,IACtB,GAAG;AAAA,IACH,IAAI,WAAW,EAAE,MAAM,GAAG,CAAC;AAAA,IAC3B,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AACA,WAAS,MAAM,KAAK,IAAI;AACxB,EAAAE,cAAa,QAAQ;AACrB,SAAO;AACT;AAEO,SAAS,WAAW,IAAqB;AAC9C,QAAM,WAAWF,cAAa;AAC9B,QAAM,SAAS,SAAS,MAAM;AAC9B,WAAS,QAAQ,SAAS,MAAM,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AACzD,MAAI,SAAS,MAAM,SAAS,QAAQ;AAClC,IAAAE,cAAa,QAAQ;AACrB,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,SAAS,YAAyB;AACvC,SAAOF,cAAa,EAAE;AACxB;AAoBA,SAAS,YACP,MACA,SACA,MACS;AACT,MAAI,CAAC,KAAK,QAAS,QAAO;AAE1B,QAAM,IAAI,KAAK;AAEf,MAAI,EAAE,QAAQ,UAAU,CAAC,EAAE,OAAO,SAAS,QAAQ,MAAM,EAAG,QAAO;AAEnE,MAAI,EAAE,OAAO,EAAE,QAAQ,QAAQ,IAAK,QAAO;AAE3C,MAAI,EAAE,YAAY;AAChB,UAAM,QAAQ,IAAI;AAAA,MAChB,MAAM,EAAE,WAAW,QAAQ,OAAO,IAAI,IAAI;AAAA,MAC1C;AAAA,IACF;AACA,QAAI,CAAC,MAAM,KAAK,QAAQ,GAAG,EAAG,QAAO;AAAA,EACvC;AAEA,MAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,KAAK,SAAS,EAAE,GAAG,GAAI,QAAO;AAEtD,MAAI,EAAE,SAAS,EAAE,UAAU,QAAQ,MAAO,QAAO;AAEjD,SAAO;AACT;AAEA,SAAS,aAAa,SAAiB,SAA2C;AAChF,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,MAAM;AAAA,MACV,GAAG,QAAQ;AAAA,MACX,gBAAgB,QAAQ;AAAA,MACxB,mBAAmB,QAAQ;AAAA,MAC3B,kBAAkB,QAAQ;AAAA,IAC5B;AAEA,SAAK,SAAS,EAAE,SAAS,KAAO,IAAI,GAAG,CAAC,KAAK,QAAQ,WAAW;AAC9D,UAAI,KAAK;AACP,gBAAQ,EAAE,QAAQ,IAAI,SAAS,OAAO,SAAS,gBAAgB,IAAI,OAAO,GAAG,CAAC;AAAA,MAChF,OAAO;AACL,gBAAQ,EAAE,QAAQ,IAAI,SAAS,MAAM,SAAS,OAAO,KAAK,KAAK,KAAK,CAAC;AAAA,MACvE;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAEA,SAAS,YAAY,KAAa,SAA2C;AAC3E,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,OAAO,KAAK,UAAU,OAAO;AACnC,UAAM,YAAY,IAAI,IAAI,GAAG;AAC7B,UAAM,QAAQ,UAAU,aAAa,WAAW,eAAe;AAE/D,UAAM,MAAM;AAAA,MACV;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,kBAAkB,OAAO,WAAW,IAAI;AAAA,UACxC,cAAc;AAAA,QAChB;AAAA,QACA,SAAS;AAAA,MACX;AAAA,MACA,CAAC,QAAQ;AACP,YAAI,OAAO;AACX,YAAI,GAAG,QAAQ,CAAC,UAAW,QAAQ,KAAM;AACzC,YAAI,GAAG,OAAO,MAAM;AAClB,kBAAQ;AAAA,YACN,QAAQ;AAAA,YACR,UAAU,IAAI,cAAc,MAAM,QAAQ,IAAI,cAAc,KAAK;AAAA,YACjE,SAAS,QAAQ,IAAI,UAAU;AAAA,UACjC,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAEA,QAAI,GAAG,SAAS,CAAC,QAAQ;AACvB,cAAQ,EAAE,QAAQ,IAAI,SAAS,OAAO,SAAS,eAAe,IAAI,OAAO,GAAG,CAAC;AAAA,IAC/E,CAAC;AACD,QAAI,GAAG,WAAW,MAAM;AACtB,UAAI,QAAQ;AACZ,cAAQ,EAAE,QAAQ,IAAI,SAAS,OAAO,SAAS,eAAe,CAAC;AAAA,IACjE,CAAC;AACD,QAAI,MAAM,IAAI;AACd,QAAI,IAAI;AAAA,EACV,CAAC;AACH;AAEA,SAAS,cACP,QACA,SAAiB,UACI;AACrB,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,MAAM,SAAS,QAAQ,EAAE;AAC/B,QAAI,CAAC,MAAM,GAAG,GAAG;AACf,UAAI;AACF,gBAAQ,KAAK,KAAK,MAAwB;AAC1C,gBAAQ,EAAE,QAAQ,IAAI,SAAS,MAAM,SAAS,UAAU,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAAA,MACvF,SAAS,KAAK;AACZ,gBAAQ,EAAE,QAAQ,IAAI,SAAS,OAAO,SAAS,iBAAiB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,GAAG,CAAC;AAAA,MACtH;AACA;AAAA,IACF;AAEA,SAAK,aAAa,MAAM,KAAK,EAAE,SAAS,IAAK,GAAG,CAAC,KAAK,WAAW;AAC/D,UAAI,OAAO,CAAC,OAAO,KAAK,GAAG;AACzB,gBAAQ,EAAE,QAAQ,IAAI,SAAS,OAAO,SAAS,YAAY,MAAM,cAAc,CAAC;AAChF;AAAA,MACF;AACA,YAAM,OAAO,OAAO,KAAK,EAAE,MAAM,IAAI,EAAE,IAAI,CAAC,MAAM,SAAS,EAAE,KAAK,GAAG,EAAE,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACjG,UAAI,OAAO;AACX,iBAAW,KAAK,MAAM;AACpB,YAAI;AACF,kBAAQ,KAAK,GAAG,MAAwB;AACxC;AAAA,QACF,QAAQ;AAAA,QAAyB;AAAA,MACnC;AACA,cAAQ,EAAE,QAAQ,IAAI,SAAS,OAAO,GAAG,SAAS,UAAU,MAAM,YAAY,IAAI,eAAe,CAAC;AAAA,IACpG,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAe,YACb,MACA,SACqB;AACrB,MAAI;AAEJ,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,eAAS,KAAK,UACV,MAAM,aAAa,KAAK,SAAS,OAAO,IACxC,EAAE,QAAQ,KAAK,IAAI,SAAS,OAAO,SAAS,uBAAuB;AACvE;AAAA,IACF,KAAK;AACH,eAAS,KAAK,MACV,MAAM,YAAY,KAAK,KAAK,OAAO,IACnC,EAAE,QAAQ,KAAK,IAAI,SAAS,OAAO,SAAS,mBAAmB;AACnE;AAAA,IACF,KAAK;AACH,eAAS,KAAK,SACV,MAAM,cAAc,KAAK,OAAO,QAAQ,KAAK,OAAO,MAAM,IAC1D,EAAE,QAAQ,KAAK,IAAI,SAAS,OAAO,SAAS,6BAA6B;AAC7E;AAAA,IACF;AACE,eAAS,EAAE,QAAQ,KAAK,IAAI,SAAS,OAAO,SAAS,sBAAsB,KAAK,IAAI,GAAG;AAAA,EAC3F;AAEA,SAAO,SAAS,KAAK;AACrB,SAAO;AACT;AAMA,eAAsB,UACpB,SACA,MACuB;AACvB,QAAM,QAAQ,UAAU;AACxB,QAAM,WAAW,MAAM,OAAO,CAAC,MAAM,YAAY,GAAG,SAAS,IAAI,CAAC;AAElE,MAAI,SAAS,WAAW,EAAG,QAAO,CAAC;AAEnC,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,SAAS,IAAI,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC;AAAA,EAC7C;AAEA,QAAM,cAA4B,CAAC;AACnC,aAAW,KAAK,SAAS;AACvB,QAAI,EAAE,WAAW,aAAa;AAC5B,kBAAY,KAAK,EAAE,KAAK;AAAA,IAC1B,OAAO;AACL,kBAAY,KAAK;AAAA,QACf,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,SAAS,EAAE,QAAQ,WAAW;AAAA,MAChC,CAAC;AAAA,IACH;AAAA,EACF;AAEA,aAAW,KAAK,aAAa;AAC3B,QAAI;AACF,eAAS;AAAA,QACP,QAAQ;AAAA,QACR,KAAK,QAAQ;AAAA,QACb,OAAO,QAAQ;AAAA,QACf,QAAQ,QAAQ;AAAA,QAChB,QAAQ,QAAQ,EAAE,MAAM,IAAI,EAAE,UAAU,OAAO,MAAM,WAAM,EAAE,OAAO;AAAA,MACtE,CAAC;AAAA,IACH,QAAQ;AAAA,IAAqC;AAAA,EAC/C;AAEA,SAAO;AACT;;;AHnQA,SAAS,aAAa,SAAiB,KAAqC;AAC1E,QAAM,QAAQ,IAAI,MAAM,SAAS,GAAG;AACpC,QAAM,MAAM,MAAM,YAAY;AAC9B,MAAI,QAAQ,KAAM,QAAO;AAEzB,QAAM,WAAW,cAAc,GAAG;AAClC,SAAO,YAAY,WAAW,GAAG;AACnC;AAEA,SAAS,cACP,SACA,KACA,UACM;AACN,QAAM,QAAQ,IAAI,MAAM,SAAS,GAAG;AACpC,QAAM,YAAY,kBAAkB,QAAQ,CAAC;AAC/C;AAEA,SAAS,WAAW,MAA+C;AACjE,MAAI,KAAK,IAAK,QAAO,KAAK;AAC1B,QAAM,SAAS,oBAAoB,EAAE,aAAa,KAAK,YAAY,CAAC;AACpE,SAAO,QAAQ;AACjB;AAMO,SAAS,UACd,KACA,OAAuB,CAAC,GACT;AACf,QAAM,SAAS,aAAa,IAAI;AAChC,QAAM,MAAM,WAAW,IAAI;AAC3B,QAAM,SAAS,KAAK,UAAU;AAE9B,aAAW,EAAE,SAAS,MAAM,KAAK,QAAQ;AACvC,UAAM,WAAW,aAAa,SAAS,GAAG;AAC1C,QAAI,CAAC,SAAU;AAGf,UAAM,QAAQ,WAAW,QAAQ;AACjC,QAAI,MAAM,WAAW;AACnB,eAAS;AAAA,QACP,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,MACV,CAAC;AACD;AAAA,IACF;AAGA,UAAM,QAAQ,cAAc,UAAU,GAAG;AACzC,QAAI,UAAU,KAAM;AAGpB,UAAM,UAAU,aAAa,QAAQ;AACrC,kBAAc,SAAS,KAAK,OAAO;AAEnC,aAAS,EAAE,QAAQ,QAAQ,KAAK,OAAO,KAAK,OAAO,CAAC;AAEpD,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAKO,SAAS,YACd,KACA,OAAuB,CAAC,GAC4B;AACpD,QAAM,SAAS,aAAa,IAAI;AAEhC,aAAW,EAAE,SAAS,MAAM,KAAK,QAAQ;AACvC,UAAM,WAAW,aAAa,SAAS,GAAG;AAC1C,QAAI,SAAU,QAAO,EAAE,UAAU,MAAM;AAAA,EACzC;AAEA,SAAO;AACT;AAKO,SAAS,UACd,KACA,OACA,OAAyB,CAAC,GACpB;AACN,QAAM,QAAQ,KAAK,SAAS;AAC5B,QAAM,SAAS,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC;AAC9C,QAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAC5B,QAAM,SAAS,KAAK,UAAU;AAG9B,QAAM,WAAW,aAAa,SAAS,GAAG;AAE1C,MAAI;AAEJ,QAAM,SAAS,KAAK,kBAAkB,UAAU,KAAK;AACrD,QAAM,SAAS,KAAK,kBAAkB,UAAU,KAAK;AACrD,QAAM,OAAO,KAAK,YAAY,UAAU,KAAK;AAE7C,MAAI,KAAK,QAAQ;AACf,eAAW,eAAe,IAAI;AAAA,MAC5B,QAAQ,KAAK;AAAA,MACb,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,MAClB,MAAM,KAAK;AAAA,MACX,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,WAAW,UAAU,KAAK;AAAA,MAC1B,gBAAgB;AAAA,MAChB,gBAAgB;AAAA,MAChB,UAAU;AAAA,IACZ,CAAC;AAAA,EACH,OAAO;AACL,eAAW,eAAe,OAAO;AAAA,MAC/B,aAAa,KAAK;AAAA,MAClB,MAAM,KAAK;AAAA,MACX,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,WAAW,UAAU,KAAK;AAAA,MAC1B,gBAAgB;AAAA,MAChB,gBAAgB;AAAA,MAChB,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAGA,MAAI,UAAU;AACZ,aAAS,KAAK,YAAY,SAAS,KAAK;AACxC,aAAS,KAAK,cAAc,SAAS,KAAK;AAAA,EAC5C;AAEA,gBAAc,SAAS,KAAK,QAAQ;AACpC,WAAS,EAAE,QAAQ,SAAS,KAAK,OAAO,OAAO,CAAC;AAGhD,QAAM,YAAY,cAAc,EAAE,SAAS,IAAI,CAAC;AAChD,aAAW,UAAU,WAAW;AAC9B,QAAI;AACF,YAAM,iBAAiB,aAAa,OAAO,SAAS,OAAO,GAAG;AAC9D,UAAI,gBAAgB;AAClB,YAAI,KAAK,QAAQ;AACf,yBAAe,SAAS,KAAK;AAAA,QAC/B,OAAO;AACL,yBAAe,QAAQ;AAAA,QACzB;AACA,uBAAe,KAAK,aAAY,oBAAI,KAAK,GAAE,YAAY;AACvD,sBAAc,OAAO,SAAS,OAAO,KAAK,cAAc;AACxD,iBAAS;AAAA,UACP,QAAQ;AAAA,UACR,KAAK,OAAO;AAAA,UACZ,OAAO;AAAA,UACP;AAAA,UACA,QAAQ,mBAAmB,GAAG;AAAA,QAChC,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,YAAU;AAAA,IACR,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC;AAAA,EACF,GAAG,SAAS,KAAK,IAAI,EAAE,MAAM,MAAM;AAAA,EAAC,CAAC;AACvC;AAKO,SAAS,aACd,KACA,OAAuB,CAAC,GACf;AACT,QAAM,SAAS,aAAa,IAAI;AAChC,QAAM,SAAS,KAAK,UAAU;AAC9B,MAAI,UAAU;AAEd,aAAW,EAAE,SAAS,MAAM,KAAK,QAAQ;AACvC,UAAM,QAAQ,IAAI,MAAM,SAAS,GAAG;AACpC,QAAI;AACF,UAAI,MAAM,iBAAiB,GAAG;AAC5B,kBAAU;AACV,iBAAS,EAAE,QAAQ,UAAU,KAAK,OAAO,OAAO,CAAC;AACjD,kBAAU;AAAA,UACR,QAAQ;AAAA,UACR;AAAA,UACA;AAAA,UACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,UAClC;AAAA,QACF,CAAC,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MACnB;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,UACd,KACA,OAAuB,CAAC,GACf;AACT,QAAM,SAAS,aAAa,IAAI;AAEhC,aAAW,EAAE,QAAQ,KAAK,QAAQ;AAChC,UAAM,WAAW,aAAa,SAAS,GAAG;AAC1C,QAAI,UAAU;AACZ,YAAM,QAAQ,WAAW,QAAQ;AACjC,UAAI,CAAC,MAAM,UAAW,QAAO;AAAA,IAC/B;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,YAAY,OAAuB,CAAC,GAAkB;AACpE,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,WAAgD,CAAC;AAEvD,MAAI,CAAC,KAAK,SAAS,KAAK,UAAU,UAAU;AAC1C,aAAS,KAAK,EAAE,SAAS,cAAc,GAAG,OAAO,SAAS,CAAC;AAAA,EAC7D;AAEA,OAAK,CAAC,KAAK,SAAS,KAAK,UAAU,cAAc,KAAK,aAAa;AACjE,aAAS,KAAK;AAAA,MACZ,SAAS,eAAe,KAAK,WAAW;AAAA,MACxC,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,QAAM,UAAyB,CAAC;AAChC,QAAM,OAAO,oBAAI,IAAY;AAE7B,aAAW,EAAE,SAAS,MAAM,KAAK,UAAU;AACzC,QAAI;AACF,YAAM,cAAc,gBAAgB,OAAO;AAC3C,iBAAW,QAAQ,aAAa;AAC9B,cAAM,KAAK,GAAG,KAAK,IAAI,KAAK,OAAO;AACnC,YAAI,KAAK,IAAI,EAAE,EAAG;AAClB,aAAK,IAAI,EAAE;AAEX,cAAM,WAAW,cAAc,KAAK,QAAQ,KAAK,WAAW,KAAK,QAAQ;AACzE,cAAM,QAAQ,WAAW,QAAQ;AAEjC,gBAAQ,KAAK;AAAA,UACX,KAAK,KAAK;AAAA,UACV;AAAA,UACA;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,MAAI,CAAC,KAAK,QAAQ;AAChB,aAAS,EAAE,QAAQ,QAAQ,OAAO,CAAC;AAAA,EACrC;AAEA,SAAO,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,IAAI,cAAc,EAAE,GAAG,CAAC;AAC1D;AAMO,SAAS,cACd,OAAuF,CAAC,GAChF;AACR,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,MAAM,WAAW,IAAI;AAC3B,MAAI,UAAU,YAAY,IAAI;AAC9B,QAAM,SAAS,KAAK,UAAU;AAE9B,MAAI,KAAK,MAAM,QAAQ;AACrB,UAAM,SAAS,IAAI,IAAI,KAAK,IAAI;AAChC,cAAU,QAAQ,OAAO,CAAC,MAAM,OAAO,IAAI,EAAE,GAAG,CAAC;AAAA,EACnD;AAEA,MAAI,KAAK,MAAM,QAAQ;AACrB,cAAU,QAAQ;AAAA,MAAO,CAAC,MACxB,KAAK,KAAM,KAAK,CAAC,MAAM,EAAE,UAAU,KAAK,MAAM,SAAS,CAAC,CAAC;AAAA,IAC3D;AAAA,EACF;AAEA,QAAM,SAAS,oBAAI,IAAoB;AAEvC,QAAM,gBAAgB,QAAQ,OAAO,CAAC,MAAM,EAAE,UAAU,QAAQ;AAChE,QAAM,iBAAiB,QAAQ,OAAO,CAAC,MAAM,EAAE,UAAU,SAAS;AAElE,aAAW,SAAS,CAAC,GAAG,eAAe,GAAG,cAAc,GAAG;AACzD,QAAI,MAAM,UAAU;AAClB,YAAM,QAAQ,WAAW,MAAM,QAAQ;AACvC,UAAI,MAAM,UAAW;AAErB,YAAM,QAAQ,cAAc,MAAM,UAAU,GAAG;AAC/C,UAAI,UAAU,MAAM;AAClB,eAAO,IAAI,MAAM,KAAK,KAAK;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAEA,WAAS,EAAE,QAAQ,UAAU,QAAQ,QAAQ,UAAU,MAAM,GAAG,CAAC;AAEjE,MAAI,WAAW,QAAQ;AACrB,UAAM,MAA8B,CAAC;AACrC,eAAW,CAAC,KAAK,KAAK,KAAK,QAAQ;AACjC,UAAI,GAAG,IAAI;AAAA,IACb;AACA,WAAO,KAAK,UAAU,KAAK,MAAM,CAAC;AAAA,EACpC;AAEA,QAAM,QAAkB,CAAC;AACzB,aAAW,CAAC,KAAK,KAAK,KAAK,QAAQ;AACjC,UAAM,UAAU,MACb,QAAQ,OAAO,MAAM,EACrB,QAAQ,MAAM,KAAK,EACnB,QAAQ,OAAO,KAAK;AACvB,UAAM,KAAK,GAAG,GAAG,KAAK,OAAO,GAAG;AAAA,EAClC;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAKO,SAAS,gBACd,WACA,YACA,WACA,YACM;AACN,QAAM,eAAe,aAAa,EAAE,GAAG,YAAY,OAAO,WAAW,SAAS,SAAS,CAAC;AACxF,QAAM,eAAe,aAAa,EAAE,GAAG,YAAY,OAAO,WAAW,SAAS,SAAS,CAAC;AAExF,QAAM,SAAS,EAAE,SAAS,aAAa,CAAC,EAAE,SAAS,KAAK,UAAU;AAClE,QAAM,SAAS,EAAE,SAAS,aAAa,CAAC,EAAE,SAAS,KAAK,UAAU;AAElE,WAAa,QAAQ,MAAM;AAC3B,WAAS;AAAA,IACP,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,QAAQ,WAAW,UAAU;AAAA,IAC7B,QAAQ,kBAAkB,SAAS;AAAA,EACrC,CAAC;AACH;AAKO,SAAS,mBACd,WACA,YACA,WACA,YACM;AACN,QAAM,eAAe,aAAa,EAAE,GAAG,YAAY,OAAO,WAAW,SAAS,SAAS,CAAC;AACxF,QAAM,eAAe,aAAa,EAAE,GAAG,YAAY,OAAO,WAAW,SAAS,SAAS,CAAC;AAExF,QAAM,SAAS,EAAE,SAAS,aAAa,CAAC,EAAE,SAAS,KAAK,UAAU;AAClE,QAAM,SAAS,EAAE,SAAS,aAAa,CAAC,EAAE,SAAS,KAAK,UAAU;AAElE,cAAgB,QAAQ,MAAM;AAC9B,WAAS;AAAA,IACP,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,QAAQ,WAAW,UAAU;AAAA,IAC7B,QAAQ,qBAAqB,SAAS;AAAA,EACxC,CAAC;AACH;;;AIrbA,IAAM,cAAc,oBAAI,IAAyB;AAEjD,IAAI,kBAAyD;AAE7D,SAAS,gBAAsB;AAC7B,MAAI,gBAAiB;AACrB,oBAAkB,YAAY,MAAM;AAClC,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW,CAAC,IAAI,KAAK,KAAK,aAAa;AACrC,UAAI,MAAM,aAAa,OAAO,MAAM,WAAW;AAC7C,oBAAY,OAAO,EAAE;AAAA,MACvB;AAAA,IACF;AACA,QAAI,YAAY,SAAS,KAAK,iBAAiB;AAC7C,oBAAc,eAAe;AAC7B,wBAAkB;AAAA,IACpB;AAAA,EACF,GAAG,GAAI;AAGP,MAAI,mBAAmB,OAAO,oBAAoB,YAAY,WAAW,iBAAiB;AACxF,oBAAgB,MAAM;AAAA,EACxB;AACF;AAYO,SAAS,aACd,OACA,OAAsB,CAAC,GACf;AACR,QAAM,KAAK,OAAO,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AACnF,QAAM,MAAM,KAAK,IAAI;AAErB,cAAY,IAAI,IAAI;AAAA,IAClB;AAAA,IACA,WAAW;AAAA,IACX,WAAW,KAAK,aAAa,MAAM,KAAK,aAAa,MAAO;AAAA,IAC5D,aAAa;AAAA,IACb,UAAU,KAAK;AAAA,EACjB,CAAC;AAED,gBAAc;AACd,SAAO;AACT;AAMO,SAAS,WAAW,IAA2B;AACpD,QAAM,QAAQ,YAAY,IAAI,EAAE;AAChC,MAAI,CAAC,MAAO,QAAO;AAEnB,MAAI,MAAM,aAAa,KAAK,IAAI,KAAK,MAAM,WAAW;AACpD,gBAAY,OAAO,EAAE;AACrB,WAAO;AAAA,EACT;AAEA,QAAM;AAEN,MAAI,MAAM,YAAY,MAAM,eAAe,MAAM,UAAU;AACzD,UAAM,QAAQ,MAAM;AACpB,gBAAY,OAAO,EAAE;AACrB,WAAO;AAAA,EACT;AAEA,SAAO,MAAM;AACf;AAKO,SAAS,cAAc,IAAqB;AACjD,SAAO,YAAY,OAAO,EAAE;AAC9B;AAKO,SAAS,aAMZ;AACF,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,SAAwC,CAAC;AAE/C,aAAW,CAAC,IAAI,KAAK,KAAK,aAAa;AACrC,QAAI,MAAM,aAAa,OAAO,MAAM,WAAW;AAC7C,kBAAY,OAAO,EAAE;AACrB;AAAA,IACF;AACA,WAAO,KAAK;AAAA,MACV;AAAA,MACA,WAAW,MAAM;AAAA,MACjB,WAAW,MAAM;AAAA,MACjB,aAAa,MAAM;AAAA,MACnB,UAAU,MAAM;AAAA,IAClB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;","names":["existsSync","readFileSync","join","existsSync","readFileSync","mkdirSync","join","homedir","existsSync","readFileSync","writeFileSync","mkdirSync","join","homedir","getRegistryPath","join","homedir","existsSync","mkdirSync","loadRegistry","readFileSync","saveRegistry","writeFileSync"]}
|
|
@@ -20,7 +20,10 @@ function createEnvelope(value, opts) {
|
|
|
20
20
|
description: opts?.description,
|
|
21
21
|
tags: opts?.tags,
|
|
22
22
|
entangled: opts?.entangled,
|
|
23
|
-
accessCount: 0
|
|
23
|
+
accessCount: 0,
|
|
24
|
+
rotationFormat: opts?.rotationFormat,
|
|
25
|
+
rotationPrefix: opts?.rotationPrefix,
|
|
26
|
+
provider: opts?.provider
|
|
24
27
|
}
|
|
25
28
|
};
|
|
26
29
|
}
|
|
@@ -173,7 +176,7 @@ function collapseEnvironment(ctx = {}) {
|
|
|
173
176
|
const branch = detectGitBranch(ctx.projectPath);
|
|
174
177
|
if (branch) {
|
|
175
178
|
const branchMap = { ...BRANCH_ENV_MAP, ...config?.branchMap };
|
|
176
|
-
const mapped = branchMap[branch];
|
|
179
|
+
const mapped = branchMap[branch] ?? matchGlob(branchMap, branch);
|
|
177
180
|
if (mapped) {
|
|
178
181
|
return { env: mapped, source: "git-branch" };
|
|
179
182
|
}
|
|
@@ -183,6 +186,16 @@ function collapseEnvironment(ctx = {}) {
|
|
|
183
186
|
}
|
|
184
187
|
return null;
|
|
185
188
|
}
|
|
189
|
+
function matchGlob(branchMap, branch) {
|
|
190
|
+
for (const [pattern, env] of Object.entries(branchMap)) {
|
|
191
|
+
if (!pattern.includes("*")) continue;
|
|
192
|
+
const regex = new RegExp(
|
|
193
|
+
"^" + pattern.replace(/\*/g, ".*") + "$"
|
|
194
|
+
);
|
|
195
|
+
if (regex.test(branch)) return env;
|
|
196
|
+
}
|
|
197
|
+
return void 0;
|
|
198
|
+
}
|
|
186
199
|
function mapEnvName(raw) {
|
|
187
200
|
const lower = raw.toLowerCase();
|
|
188
201
|
if (lower === "production") return "prod";
|
|
@@ -323,6 +336,13 @@ function entangle(source, target) {
|
|
|
323
336
|
saveRegistry(registry);
|
|
324
337
|
}
|
|
325
338
|
}
|
|
339
|
+
function disentangle(source, target) {
|
|
340
|
+
const registry = loadRegistry();
|
|
341
|
+
registry.pairs = registry.pairs.filter(
|
|
342
|
+
(p) => !(p.source.service === source.service && p.source.key === source.key && p.target.service === target.service && p.target.key === target.key || p.source.service === target.service && p.source.key === target.key && p.target.service === source.service && p.target.key === source.key)
|
|
343
|
+
);
|
|
344
|
+
saveRegistry(registry);
|
|
345
|
+
}
|
|
326
346
|
function findEntangled(source) {
|
|
327
347
|
const registry = loadRegistry();
|
|
328
348
|
return registry.pairs.filter(
|
|
@@ -373,6 +393,229 @@ function resolveScope(opts) {
|
|
|
373
393
|
return [{ scope: "global", service: globalService() }];
|
|
374
394
|
}
|
|
375
395
|
|
|
396
|
+
// src/core/hooks.ts
|
|
397
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "fs";
|
|
398
|
+
import { join as join4 } from "path";
|
|
399
|
+
import { homedir as homedir3 } from "os";
|
|
400
|
+
import { exec } from "child_process";
|
|
401
|
+
import { request as httpsRequest } from "https";
|
|
402
|
+
import { request as httpRequest } from "http";
|
|
403
|
+
import { randomUUID } from "crypto";
|
|
404
|
+
function getRegistryPath2() {
|
|
405
|
+
const dir = join4(homedir3(), ".config", "q-ring");
|
|
406
|
+
if (!existsSync4(dir)) {
|
|
407
|
+
mkdirSync3(dir, { recursive: true });
|
|
408
|
+
}
|
|
409
|
+
return join4(dir, "hooks.json");
|
|
410
|
+
}
|
|
411
|
+
function loadRegistry2() {
|
|
412
|
+
const path = getRegistryPath2();
|
|
413
|
+
if (!existsSync4(path)) {
|
|
414
|
+
return { hooks: [] };
|
|
415
|
+
}
|
|
416
|
+
try {
|
|
417
|
+
return JSON.parse(readFileSync4(path, "utf8"));
|
|
418
|
+
} catch {
|
|
419
|
+
return { hooks: [] };
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
function saveRegistry2(registry) {
|
|
423
|
+
writeFileSync2(getRegistryPath2(), JSON.stringify(registry, null, 2));
|
|
424
|
+
}
|
|
425
|
+
function registerHook(entry) {
|
|
426
|
+
const registry = loadRegistry2();
|
|
427
|
+
const hook = {
|
|
428
|
+
...entry,
|
|
429
|
+
id: randomUUID().slice(0, 8),
|
|
430
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
431
|
+
};
|
|
432
|
+
registry.hooks.push(hook);
|
|
433
|
+
saveRegistry2(registry);
|
|
434
|
+
return hook;
|
|
435
|
+
}
|
|
436
|
+
function removeHook(id) {
|
|
437
|
+
const registry = loadRegistry2();
|
|
438
|
+
const before = registry.hooks.length;
|
|
439
|
+
registry.hooks = registry.hooks.filter((h) => h.id !== id);
|
|
440
|
+
if (registry.hooks.length < before) {
|
|
441
|
+
saveRegistry2(registry);
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
function listHooks() {
|
|
447
|
+
return loadRegistry2().hooks;
|
|
448
|
+
}
|
|
449
|
+
function enableHook(id) {
|
|
450
|
+
const registry = loadRegistry2();
|
|
451
|
+
const hook = registry.hooks.find((h) => h.id === id);
|
|
452
|
+
if (!hook) return false;
|
|
453
|
+
hook.enabled = true;
|
|
454
|
+
saveRegistry2(registry);
|
|
455
|
+
return true;
|
|
456
|
+
}
|
|
457
|
+
function disableHook(id) {
|
|
458
|
+
const registry = loadRegistry2();
|
|
459
|
+
const hook = registry.hooks.find((h) => h.id === id);
|
|
460
|
+
if (!hook) return false;
|
|
461
|
+
hook.enabled = false;
|
|
462
|
+
saveRegistry2(registry);
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
function matchesHook(hook, payload, tags) {
|
|
466
|
+
if (!hook.enabled) return false;
|
|
467
|
+
const m = hook.match;
|
|
468
|
+
if (m.action?.length && !m.action.includes(payload.action)) return false;
|
|
469
|
+
if (m.key && m.key !== payload.key) return false;
|
|
470
|
+
if (m.keyPattern) {
|
|
471
|
+
const regex = new RegExp(
|
|
472
|
+
"^" + m.keyPattern.replace(/\*/g, ".*") + "$",
|
|
473
|
+
"i"
|
|
474
|
+
);
|
|
475
|
+
if (!regex.test(payload.key)) return false;
|
|
476
|
+
}
|
|
477
|
+
if (m.tag && (!tags || !tags.includes(m.tag))) return false;
|
|
478
|
+
if (m.scope && m.scope !== payload.scope) return false;
|
|
479
|
+
return true;
|
|
480
|
+
}
|
|
481
|
+
function executeShell(command, payload) {
|
|
482
|
+
return new Promise((resolve) => {
|
|
483
|
+
const env = {
|
|
484
|
+
...process.env,
|
|
485
|
+
QRING_HOOK_KEY: payload.key,
|
|
486
|
+
QRING_HOOK_ACTION: payload.action,
|
|
487
|
+
QRING_HOOK_SCOPE: payload.scope
|
|
488
|
+
};
|
|
489
|
+
exec(command, { timeout: 3e4, env }, (err, stdout, stderr) => {
|
|
490
|
+
if (err) {
|
|
491
|
+
resolve({ hookId: "", success: false, message: `Shell error: ${err.message}` });
|
|
492
|
+
} else {
|
|
493
|
+
resolve({ hookId: "", success: true, message: stdout.trim() || "OK" });
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
function executeHttp(url, payload) {
|
|
499
|
+
return new Promise((resolve) => {
|
|
500
|
+
const body = JSON.stringify(payload);
|
|
501
|
+
const parsedUrl = new URL(url);
|
|
502
|
+
const reqFn = parsedUrl.protocol === "https:" ? httpsRequest : httpRequest;
|
|
503
|
+
const req = reqFn(
|
|
504
|
+
url,
|
|
505
|
+
{
|
|
506
|
+
method: "POST",
|
|
507
|
+
headers: {
|
|
508
|
+
"Content-Type": "application/json",
|
|
509
|
+
"Content-Length": Buffer.byteLength(body),
|
|
510
|
+
"User-Agent": "q-ring-hooks/1.0"
|
|
511
|
+
},
|
|
512
|
+
timeout: 1e4
|
|
513
|
+
},
|
|
514
|
+
(res) => {
|
|
515
|
+
let data = "";
|
|
516
|
+
res.on("data", (chunk) => data += chunk);
|
|
517
|
+
res.on("end", () => {
|
|
518
|
+
resolve({
|
|
519
|
+
hookId: "",
|
|
520
|
+
success: (res.statusCode ?? 0) >= 200 && (res.statusCode ?? 0) < 300,
|
|
521
|
+
message: `HTTP ${res.statusCode}`
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
);
|
|
526
|
+
req.on("error", (err) => {
|
|
527
|
+
resolve({ hookId: "", success: false, message: `HTTP error: ${err.message}` });
|
|
528
|
+
});
|
|
529
|
+
req.on("timeout", () => {
|
|
530
|
+
req.destroy();
|
|
531
|
+
resolve({ hookId: "", success: false, message: "HTTP timeout" });
|
|
532
|
+
});
|
|
533
|
+
req.write(body);
|
|
534
|
+
req.end();
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
function executeSignal(target, signal = "SIGHUP") {
|
|
538
|
+
return new Promise((resolve) => {
|
|
539
|
+
const pid = parseInt(target, 10);
|
|
540
|
+
if (!isNaN(pid)) {
|
|
541
|
+
try {
|
|
542
|
+
process.kill(pid, signal);
|
|
543
|
+
resolve({ hookId: "", success: true, message: `Signal ${signal} sent to PID ${pid}` });
|
|
544
|
+
} catch (err) {
|
|
545
|
+
resolve({ hookId: "", success: false, message: `Signal error: ${err instanceof Error ? err.message : String(err)}` });
|
|
546
|
+
}
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
exec(`pgrep -f "${target}"`, { timeout: 5e3 }, (err, stdout) => {
|
|
550
|
+
if (err || !stdout.trim()) {
|
|
551
|
+
resolve({ hookId: "", success: false, message: `Process "${target}" not found` });
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const pids = stdout.trim().split("\n").map((p) => parseInt(p.trim(), 10)).filter((p) => !isNaN(p));
|
|
555
|
+
let sent = 0;
|
|
556
|
+
for (const p of pids) {
|
|
557
|
+
try {
|
|
558
|
+
process.kill(p, signal);
|
|
559
|
+
sent++;
|
|
560
|
+
} catch {
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
resolve({ hookId: "", success: sent > 0, message: `Signal ${signal} sent to ${sent} process(es)` });
|
|
564
|
+
});
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
async function executeHook(hook, payload) {
|
|
568
|
+
let result;
|
|
569
|
+
switch (hook.type) {
|
|
570
|
+
case "shell":
|
|
571
|
+
result = hook.command ? await executeShell(hook.command, payload) : { hookId: hook.id, success: false, message: "No command specified" };
|
|
572
|
+
break;
|
|
573
|
+
case "http":
|
|
574
|
+
result = hook.url ? await executeHttp(hook.url, payload) : { hookId: hook.id, success: false, message: "No URL specified" };
|
|
575
|
+
break;
|
|
576
|
+
case "signal":
|
|
577
|
+
result = hook.signal ? await executeSignal(hook.signal.target, hook.signal.signal) : { hookId: hook.id, success: false, message: "No signal target specified" };
|
|
578
|
+
break;
|
|
579
|
+
default:
|
|
580
|
+
result = { hookId: hook.id, success: false, message: `Unknown hook type: ${hook.type}` };
|
|
581
|
+
}
|
|
582
|
+
result.hookId = hook.id;
|
|
583
|
+
return result;
|
|
584
|
+
}
|
|
585
|
+
async function fireHooks(payload, tags) {
|
|
586
|
+
const hooks = listHooks();
|
|
587
|
+
const matching = hooks.filter((h) => matchesHook(h, payload, tags));
|
|
588
|
+
if (matching.length === 0) return [];
|
|
589
|
+
const results = await Promise.allSettled(
|
|
590
|
+
matching.map((h) => executeHook(h, payload))
|
|
591
|
+
);
|
|
592
|
+
const hookResults = [];
|
|
593
|
+
for (const r of results) {
|
|
594
|
+
if (r.status === "fulfilled") {
|
|
595
|
+
hookResults.push(r.value);
|
|
596
|
+
} else {
|
|
597
|
+
hookResults.push({
|
|
598
|
+
hookId: "unknown",
|
|
599
|
+
success: false,
|
|
600
|
+
message: r.reason?.message ?? "Hook execution failed"
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
for (const r of hookResults) {
|
|
605
|
+
try {
|
|
606
|
+
logAudit({
|
|
607
|
+
action: "write",
|
|
608
|
+
key: payload.key,
|
|
609
|
+
scope: payload.scope,
|
|
610
|
+
source: payload.source,
|
|
611
|
+
detail: `hook:${r.hookId} ${r.success ? "ok" : "fail"} \u2014 ${r.message}`
|
|
612
|
+
});
|
|
613
|
+
} catch {
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
return hookResults;
|
|
617
|
+
}
|
|
618
|
+
|
|
376
619
|
// src/core/keyring.ts
|
|
377
620
|
function readEnvelope(service, key) {
|
|
378
621
|
const entry = new Entry(service, key);
|
|
@@ -432,6 +675,9 @@ function setSecret(key, value, opts = {}) {
|
|
|
432
675
|
const source = opts.source ?? "cli";
|
|
433
676
|
const existing = readEnvelope(service, key);
|
|
434
677
|
let envelope;
|
|
678
|
+
const rotFmt = opts.rotationFormat ?? existing?.meta.rotationFormat;
|
|
679
|
+
const rotPfx = opts.rotationPrefix ?? existing?.meta.rotationPrefix;
|
|
680
|
+
const prov = opts.provider ?? existing?.meta.provider;
|
|
435
681
|
if (opts.states) {
|
|
436
682
|
envelope = createEnvelope("", {
|
|
437
683
|
states: opts.states,
|
|
@@ -440,7 +686,10 @@ function setSecret(key, value, opts = {}) {
|
|
|
440
686
|
tags: opts.tags,
|
|
441
687
|
ttlSeconds: opts.ttlSeconds,
|
|
442
688
|
expiresAt: opts.expiresAt,
|
|
443
|
-
entangled: existing?.meta.entangled
|
|
689
|
+
entangled: existing?.meta.entangled,
|
|
690
|
+
rotationFormat: rotFmt,
|
|
691
|
+
rotationPrefix: rotPfx,
|
|
692
|
+
provider: prov
|
|
444
693
|
});
|
|
445
694
|
} else {
|
|
446
695
|
envelope = createEnvelope(value, {
|
|
@@ -448,7 +697,10 @@ function setSecret(key, value, opts = {}) {
|
|
|
448
697
|
tags: opts.tags,
|
|
449
698
|
ttlSeconds: opts.ttlSeconds,
|
|
450
699
|
expiresAt: opts.expiresAt,
|
|
451
|
-
entangled: existing?.meta.entangled
|
|
700
|
+
entangled: existing?.meta.entangled,
|
|
701
|
+
rotationFormat: rotFmt,
|
|
702
|
+
rotationPrefix: rotPfx,
|
|
703
|
+
provider: prov
|
|
452
704
|
});
|
|
453
705
|
}
|
|
454
706
|
if (existing) {
|
|
@@ -480,6 +732,14 @@ function setSecret(key, value, opts = {}) {
|
|
|
480
732
|
} catch {
|
|
481
733
|
}
|
|
482
734
|
}
|
|
735
|
+
fireHooks({
|
|
736
|
+
action: "write",
|
|
737
|
+
key,
|
|
738
|
+
scope,
|
|
739
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
740
|
+
source
|
|
741
|
+
}, envelope.meta.tags).catch(() => {
|
|
742
|
+
});
|
|
483
743
|
}
|
|
484
744
|
function deleteSecret(key, opts = {}) {
|
|
485
745
|
const scopes = resolveScope(opts);
|
|
@@ -491,6 +751,14 @@ function deleteSecret(key, opts = {}) {
|
|
|
491
751
|
if (entry.deleteCredential()) {
|
|
492
752
|
deleted = true;
|
|
493
753
|
logAudit({ action: "delete", key, scope, source });
|
|
754
|
+
fireHooks({
|
|
755
|
+
action: "delete",
|
|
756
|
+
key,
|
|
757
|
+
scope,
|
|
758
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
759
|
+
source
|
|
760
|
+
}).catch(() => {
|
|
761
|
+
});
|
|
494
762
|
}
|
|
495
763
|
} catch {
|
|
496
764
|
}
|
|
@@ -546,6 +814,48 @@ function listSecrets(opts = {}) {
|
|
|
546
814
|
}
|
|
547
815
|
return results.sort((a, b) => a.key.localeCompare(b.key));
|
|
548
816
|
}
|
|
817
|
+
function exportSecrets(opts = {}) {
|
|
818
|
+
const format = opts.format ?? "env";
|
|
819
|
+
const env = resolveEnv(opts);
|
|
820
|
+
let entries = listSecrets(opts);
|
|
821
|
+
const source = opts.source ?? "cli";
|
|
822
|
+
if (opts.keys?.length) {
|
|
823
|
+
const keySet = new Set(opts.keys);
|
|
824
|
+
entries = entries.filter((e) => keySet.has(e.key));
|
|
825
|
+
}
|
|
826
|
+
if (opts.tags?.length) {
|
|
827
|
+
entries = entries.filter(
|
|
828
|
+
(e) => opts.tags.some((t) => e.envelope?.meta.tags?.includes(t))
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
const merged = /* @__PURE__ */ new Map();
|
|
832
|
+
const globalEntries = entries.filter((e) => e.scope === "global");
|
|
833
|
+
const projectEntries = entries.filter((e) => e.scope === "project");
|
|
834
|
+
for (const entry of [...globalEntries, ...projectEntries]) {
|
|
835
|
+
if (entry.envelope) {
|
|
836
|
+
const decay = checkDecay(entry.envelope);
|
|
837
|
+
if (decay.isExpired) continue;
|
|
838
|
+
const value = collapseValue(entry.envelope, env);
|
|
839
|
+
if (value !== null) {
|
|
840
|
+
merged.set(entry.key, value);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
logAudit({ action: "export", source, detail: `format=${format}` });
|
|
845
|
+
if (format === "json") {
|
|
846
|
+
const obj = {};
|
|
847
|
+
for (const [key, value] of merged) {
|
|
848
|
+
obj[key] = value;
|
|
849
|
+
}
|
|
850
|
+
return JSON.stringify(obj, null, 2);
|
|
851
|
+
}
|
|
852
|
+
const lines = [];
|
|
853
|
+
for (const [key, value] of merged) {
|
|
854
|
+
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
|
855
|
+
lines.push(`${key}="${escaped}"`);
|
|
856
|
+
}
|
|
857
|
+
return lines.join("\n");
|
|
858
|
+
}
|
|
549
859
|
function entangleSecrets(sourceKey, sourceOpts, targetKey, targetOpts) {
|
|
550
860
|
const sourceScopes = resolveScope({ ...sourceOpts, scope: sourceOpts.scope ?? "global" });
|
|
551
861
|
const targetScopes = resolveScope({ ...targetOpts, scope: targetOpts.scope ?? "global" });
|
|
@@ -559,6 +869,19 @@ function entangleSecrets(sourceKey, sourceOpts, targetKey, targetOpts) {
|
|
|
559
869
|
detail: `entangled with ${targetKey}`
|
|
560
870
|
});
|
|
561
871
|
}
|
|
872
|
+
function disentangleSecrets(sourceKey, sourceOpts, targetKey, targetOpts) {
|
|
873
|
+
const sourceScopes = resolveScope({ ...sourceOpts, scope: sourceOpts.scope ?? "global" });
|
|
874
|
+
const targetScopes = resolveScope({ ...targetOpts, scope: targetOpts.scope ?? "global" });
|
|
875
|
+
const source = { service: sourceScopes[0].service, key: sourceKey };
|
|
876
|
+
const target = { service: targetScopes[0].service, key: targetKey };
|
|
877
|
+
disentangle(source, target);
|
|
878
|
+
logAudit({
|
|
879
|
+
action: "entangle",
|
|
880
|
+
key: sourceKey,
|
|
881
|
+
source: sourceOpts.source ?? "cli",
|
|
882
|
+
detail: `disentangled from ${targetKey}`
|
|
883
|
+
});
|
|
884
|
+
}
|
|
562
885
|
|
|
563
886
|
// src/core/tunnel.ts
|
|
564
887
|
var tunnelStore = /* @__PURE__ */ new Map();
|
|
@@ -633,21 +956,30 @@ function tunnelList() {
|
|
|
633
956
|
|
|
634
957
|
export {
|
|
635
958
|
checkDecay,
|
|
959
|
+
readProjectConfig,
|
|
636
960
|
collapseEnvironment,
|
|
637
961
|
logAudit,
|
|
638
962
|
queryAudit,
|
|
639
963
|
detectAnomalies,
|
|
640
964
|
listEntanglements,
|
|
965
|
+
registerHook,
|
|
966
|
+
removeHook,
|
|
967
|
+
listHooks,
|
|
968
|
+
enableHook,
|
|
969
|
+
disableHook,
|
|
970
|
+
fireHooks,
|
|
641
971
|
getSecret,
|
|
642
972
|
getEnvelope,
|
|
643
973
|
setSecret,
|
|
644
974
|
deleteSecret,
|
|
645
975
|
hasSecret,
|
|
646
976
|
listSecrets,
|
|
977
|
+
exportSecrets,
|
|
647
978
|
entangleSecrets,
|
|
979
|
+
disentangleSecrets,
|
|
648
980
|
tunnelCreate,
|
|
649
981
|
tunnelRead,
|
|
650
982
|
tunnelDestroy,
|
|
651
983
|
tunnelList
|
|
652
984
|
};
|
|
653
|
-
//# sourceMappingURL=chunk-
|
|
985
|
+
//# sourceMappingURL=chunk-IGNU622R.js.map
|