@mastra/code-sdk 1.9.0-alpha.9 → 1.10.0-alpha.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 +23 -0
- package/dist/agent-connections/tools.d.ts.map +1 -1
- package/dist/agent-connections/tools.js +5 -5
- package/dist/agent-connections/tools.js.map +1 -1
- package/dist/agents/tools.d.ts +1 -1
- package/dist/agents/tools.d.ts.map +1 -1
- package/dist/agents/tools.js +2 -1
- package/dist/agents/tools.js.map +1 -1
- package/dist/analytics.d.ts.map +1 -1
- package/dist/analytics.js +2 -3
- package/dist/analytics.js.map +1 -1
- package/dist/auth/account-rotation-processor.d.ts.map +1 -1
- package/dist/auth/account-rotation-processor.js +17 -10
- package/dist/auth/account-rotation-processor.js.map +1 -1
- package/dist/auth/providers/kimi-coding.d.ts.map +1 -1
- package/dist/auth/providers/kimi-coding.js +1 -2
- package/dist/auth/providers/kimi-coding.js.map +1 -1
- package/dist/auth/providers/openai-codex.d.ts +1 -1
- package/dist/auth/providers/openai-codex.d.ts.map +1 -1
- package/dist/auth/providers/openai-codex.js +7 -19
- package/dist/auth/providers/openai-codex.js.map +1 -1
- package/dist/auth/storage.js +3 -3
- package/dist/auth/storage.js.map +1 -1
- package/dist/experimental-agent.d.ts +11 -0
- package/dist/experimental-agent.d.ts.map +1 -0
- package/dist/experimental-agent.js +36 -0
- package/dist/experimental-agent.js.map +1 -0
- package/dist/goal-manager.d.ts +39 -3
- package/dist/goal-manager.d.ts.map +1 -1
- package/dist/goal-manager.js +113 -31
- package/dist/goal-manager.js.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +95 -12
- package/dist/index.js.map +1 -1
- package/dist/knowledge-inspector.d.ts.map +1 -1
- package/dist/knowledge-inspector.js +1 -2
- package/dist/knowledge-inspector.js.map +1 -1
- package/dist/onboarding/settings.d.ts +11 -0
- package/dist/onboarding/settings.d.ts.map +1 -1
- package/dist/onboarding/settings.js +39 -5
- package/dist/onboarding/settings.js.map +1 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +6 -1
- package/dist/permissions.js.map +1 -1
- package/dist/plugins/loader.d.ts.map +1 -1
- package/dist/plugins/loader.js +1 -2
- package/dist/plugins/loader.js.map +1 -1
- package/dist/process-memory-diagnostics.d.ts.map +1 -1
- package/dist/process-memory-diagnostics.js +2 -3
- package/dist/process-memory-diagnostics.js.map +1 -1
- package/dist/schedules/index.d.ts +10 -0
- package/dist/schedules/index.d.ts.map +1 -0
- package/dist/schedules/index.js +6 -0
- package/dist/schedules/interval.d.ts +49 -0
- package/dist/schedules/interval.d.ts.map +1 -0
- package/dist/schedules/interval.js +215 -0
- package/dist/schedules/interval.js.map +1 -0
- package/dist/schedules/prompt.d.ts +36 -0
- package/dist/schedules/prompt.d.ts.map +1 -0
- package/dist/schedules/prompt.js +39 -0
- package/dist/schedules/prompt.js.map +1 -0
- package/dist/schedules/run-script.d.ts +8 -0
- package/dist/schedules/run-script.d.ts.map +1 -0
- package/dist/schedules/run-script.js +83 -0
- package/dist/schedules/run-script.js.map +1 -0
- package/dist/schedules/scheduler.d.ts +101 -0
- package/dist/schedules/scheduler.d.ts.map +1 -0
- package/dist/schedules/scheduler.js +169 -0
- package/dist/schedules/scheduler.js.map +1 -0
- package/dist/schedules/tools.d.ts +57 -0
- package/dist/schedules/tools.d.ts.map +1 -0
- package/dist/schedules/tools.js +220 -0
- package/dist/schedules/tools.js.map +1 -0
- package/dist/tools/workflows/ephemeral-memory.d.ts +21 -0
- package/dist/tools/workflows/ephemeral-memory.d.ts.map +1 -1
- package/dist/tools/workflows/ephemeral-memory.js +1 -2
- package/dist/tools/workflows/ephemeral-memory.js.map +1 -1
- package/dist/utils/discarding-scores-storage.d.ts +33 -0
- package/dist/utils/discarding-scores-storage.d.ts.map +1 -0
- package/dist/utils/discarding-scores-storage.js +54 -0
- package/dist/utils/discarding-scores-storage.js.map +1 -0
- package/dist/utils/maintenance-lock.d.ts +42 -0
- package/dist/utils/maintenance-lock.d.ts.map +1 -0
- package/dist/utils/maintenance-lock.js +199 -0
- package/dist/utils/maintenance-lock.js.map +1 -0
- package/dist/utils/prune-cli.d.ts +24 -0
- package/dist/utils/prune-cli.d.ts.map +1 -0
- package/dist/utils/prune-cli.js +125 -0
- package/dist/utils/prune-cli.js.map +1 -0
- package/dist/utils/signals-pubsub.d.ts.map +1 -1
- package/dist/utils/signals-pubsub.js +1 -2
- package/dist/utils/signals-pubsub.js.map +1 -1
- package/dist/utils/storage-maintenance.d.ts.map +1 -1
- package/dist/utils/storage-maintenance.js +33 -3
- package/dist/utils/storage-maintenance.js.map +1 -1
- package/package.json +21 -15
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.js","names":[],"sources":["../../src/plugins/loader.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport { isSignalProvider } from '@mastra/core/signals';\nimport type { SignalProvider } from '@mastra/core/signals';\n\nimport type {\n MastraCodePlugin,\n MastraCodePluginConfigSchema,\n MastraCodePluginConfigValues,\n MastraCodePluginContext,\n MastraCodePluginProcessorEntries,\n MastraCodePluginRuntime,\n MastraCodePluginSignalProviderEntries,\n MastraCodePluginToolEntries,\n MastraCodePluginTools,\n MastraCodeToolRenderConfig,\n} from '../plugin.js';\nimport { getPluginRoot } from './paths.js';\nimport type { PluginPathOptions } from './paths.js';\nimport { loadPluginRegistry, mergePluginRegistries } from './registry.js';\nimport type { LoadedPlugin, LoadedPluginProcessors, PluginRegistry, ScopedInstalledPluginRecord } from './types.js';\n\nexport type LoadPluginRecordOptions = PluginPathOptions & {\n /** Lazy accessors for Mastra Code's runtime, passed on to plugin field resolvers. */\n runtime?: MastraCodePluginRuntime;\n};\n\nexport type LoadPluginsOptions = LoadPluginRecordOptions & {\n globalRegistry?: PluginRegistry;\n projectRegistry?: PluginRegistry;\n};\n\nexport async function loadPlugins(options: LoadPluginsOptions): Promise<LoadedPlugin[]> {\n const globalRegistry =\n options.globalRegistry ?? loadPluginRegistry(path.join(getPluginRoot('global', options), 'plugins.json'));\n const projectRegistry =\n options.projectRegistry ?? loadPluginRegistry(path.join(getPluginRoot('project', options), 'plugins.json'));\n const records = mergePluginRegistries(globalRegistry, projectRegistry);\n const loaded: LoadedPlugin[] = [];\n\n for (const record of records) {\n if (record.blocked) {\n loaded.push({ ...record, status: 'blocked', tools: {}, toolNames: [] });\n continue;\n }\n if (!record.enabled) {\n loaded.push({ ...record, status: 'inactive', tools: {}, toolNames: [] });\n continue;\n }\n\n loaded.push(await loadPluginRecord(record, options));\n }\n\n return markToolConflicts(loaded);\n}\n\nexport async function loadPluginRecord(\n record: ScopedInstalledPluginRecord,\n options: LoadPluginRecordOptions,\n): Promise<LoadedPlugin> {\n try {\n const entryPath = resolvePluginEntryPath(record, options);\n const plugin = await importPluginModule(entryPath);\n if (plugin.id !== record.id) {\n throw new Error(`Plugin id mismatch: registry has \"${record.id}\" but module exports \"${plugin.id}\"`);\n }\n\n const configSchema = validatePluginConfigSchema(plugin.config);\n const configValues = resolvePluginConfigValues(configSchema, record.config);\n const pluginDir = path.dirname(entryPath);\n const pluginRoot = resolvePluginRoot(record, options);\n const context: MastraCodePluginContext = {\n cwd: options.projectRoot,\n scope: record.scope,\n pluginDir,\n config: configValues,\n // Accessors, not instances: plugins load before the controller and the\n // session exist, so a plugin resolves these when it needs them, not now.\n getController: options.runtime?.getController,\n getActiveSession: options.runtime?.getActiveSession,\n getStorage: options.runtime?.getStorage,\n };\n const { tools, renderConfigs } = await resolvePluginTools(plugin, context);\n const processors = await resolvePluginProcessors(plugin, context);\n const signalProviders = await resolvePluginSignalProviders(plugin, context);\n const instructions = await resolvePluginInstructions(plugin, context);\n\n return {\n ...record,\n name: plugin.name,\n version: plugin.version ?? record.version,\n description: plugin.description,\n instructions,\n status: 'active',\n tools,\n renderConfigs,\n toolNames: Object.keys(tools).sort(),\n processors,\n signalProviders,\n skillPaths: resolveExistingAssetDirs(pluginRoot, 'skills'),\n commandPaths: resolveExistingAssetDirs(pluginRoot, 'commands'),\n configSchema,\n configValues,\n };\n } catch (error) {\n return {\n ...record,\n status: 'load failed',\n error: error instanceof Error ? error.message : String(error),\n tools: {},\n toolNames: [],\n };\n }\n}\n\nexport async function loadPluginFromEntry(entryPath: string): Promise<MastraCodePlugin> {\n return validatePluginExport(await importPluginModule(entryPath));\n}\n\nexport function resolvePluginRoot(record: ScopedInstalledPluginRecord, options: PluginPathOptions): string {\n const scopeRoot = path.resolve(getPluginRoot(record.scope, options));\n const pluginRoot = path.resolve(path.isAbsolute(record.path) ? record.path : path.join(scopeRoot, record.path));\n if (record.source === 'github' && !isInsideDirectory(pluginRoot, scopeRoot)) {\n throw new Error(`Plugin path for \"${record.id}\" must be inside the ${record.scope} plugin directory`);\n }\n return pluginRoot;\n}\n\nexport function resolvePluginEntryPath(record: ScopedInstalledPluginRecord, options: PluginPathOptions): string {\n const pluginRoot = resolvePluginRoot(record, options);\n const entryPath = path.resolve(pluginRoot, record.entry);\n if (!isInsideDirectory(entryPath, pluginRoot)) {\n throw new Error(`Plugin entry for \"${record.id}\" must be inside the plugin directory`);\n }\n return entryPath;\n}\n\nexport function isInsideDirectory(targetPath: string, root: string): boolean {\n const resolvedTarget = path.resolve(targetPath);\n const resolvedRoot = path.resolve(root);\n return resolvedTarget === resolvedRoot || resolvedTarget.startsWith(resolvedRoot + path.sep);\n}\n\nfunction resolveExistingAssetDirs(pluginRoot: string, dirname: 'skills' | 'commands'): string[] {\n const dir = path.join(pluginRoot, dirname);\n try {\n return fs.statSync(dir).isDirectory() ? [dir] : [];\n } catch {\n return [];\n }\n}\n\nasync function importPluginModule(entryPath: string): Promise<MastraCodePlugin> {\n if (path.extname(entryPath) !== '.ts') {\n throw new Error(\n `Unsupported plugin entry extension \"${path.extname(entryPath)}\". V1 plugins must use .ts entries.`,\n );\n }\n\n const url = pathToFileURL(entryPath);\n const contentHash = createHash('sha1').update(fs.readFileSync(entryPath)).digest('hex');\n const stat = fs.statSync(entryPath, { bigint: true });\n url.searchParams.set('contentHash', contentHash);\n url.searchParams.set('mtimeNs', stat.mtimeNs.toString());\n const mod = (await import(url.href)) as { default?: unknown; plugin?: unknown };\n return validatePluginExport(mod.default ?? mod.plugin);\n}\n\nfunction validatePluginExport(value: unknown): MastraCodePlugin {\n if (!value || typeof value !== 'object') {\n throw new Error('Plugin module must export a plugin object as default or named \"plugin\" export');\n }\n\n const plugin = value as MastraCodePlugin;\n if (typeof plugin.id !== 'string' || plugin.id.trim().length === 0) {\n throw new Error('Plugin id must be a non-empty string');\n }\n\n if (plugin.tools !== undefined && typeof plugin.tools !== 'object' && typeof plugin.tools !== 'function') {\n throw new Error('Plugin tools must be an object or function');\n }\n\n if (\n plugin.processors !== undefined &&\n typeof plugin.processors !== 'object' &&\n typeof plugin.processors !== 'function'\n ) {\n throw new Error('Plugin processors must be an array, object, or function');\n }\n\n if (\n plugin.signalProviders !== undefined &&\n !Array.isArray(plugin.signalProviders) &&\n typeof plugin.signalProviders !== 'function'\n ) {\n throw new Error('Plugin signal providers must be an array or function');\n }\n\n return plugin;\n}\n\nasync function resolvePluginTools(\n plugin: MastraCodePlugin,\n context: MastraCodePluginContext,\n): Promise<{ tools: MastraCodePluginTools; renderConfigs: Record<string, MastraCodeToolRenderConfig> }> {\n if (!plugin.tools) return { tools: {}, renderConfigs: {} };\n const entries = typeof plugin.tools === 'function' ? await plugin.tools(context) : plugin.tools;\n if (!entries || typeof entries !== 'object' || Array.isArray(entries)) {\n throw new Error('Plugin tools function must return an object');\n }\n return normalizePluginToolEntries(entries);\n}\n\n/** Mirrors {@link resolvePluginTools}: object-or-function, resolved with the same context. */\nasync function resolvePluginProcessors(\n plugin: MastraCodePlugin,\n context: MastraCodePluginContext,\n): Promise<LoadedPluginProcessors> {\n if (!plugin.processors) return { input: [], output: [] };\n const entries = typeof plugin.processors === 'function' ? await plugin.processors(context) : plugin.processors;\n if (!entries || typeof entries !== 'object') {\n throw new Error('Plugin processors function must return an array or object');\n }\n return normalizePluginProcessorEntries(entries);\n}\n\nasync function resolvePluginSignalProviders(\n plugin: MastraCodePlugin,\n context: MastraCodePluginContext,\n): Promise<MastraCodePluginSignalProviderEntries> {\n if (!plugin.signalProviders) return [];\n const entries =\n typeof plugin.signalProviders === 'function' ? await plugin.signalProviders(context) : plugin.signalProviders;\n if (!Array.isArray(entries)) {\n throw new Error('Plugin signal providers function must return an array');\n }\n for (const [index, provider] of entries.entries()) {\n if (!isPluginSignalProvider(provider)) {\n throw new Error(\n `Plugin signal provider at index ${index} must be a SignalProvider (an object with an id that implements connect, startPolling, stop and __registerMastra)`,\n );\n }\n }\n return entries;\n}\n\n/**\n * Structural, not `instanceof`.\n *\n * A plugin that depends on a published provider package — the motivating case, a plugin wrapping\n * `@mastra/github-signals` — installs that package's own copy of `@mastra/core`, so its provider is\n * never an instance of the `SignalProvider` class Mastra Code loaded. `isSignalProvider` would reject\n * a perfectly working provider. Nothing in the lifecycle needs class identity: the lane only calls\n * these methods, so the methods are what is checked.\n */\nfunction isPluginSignalProvider(value: unknown): value is SignalProvider<string> {\n if (isSignalProvider(value)) return true;\n if (!value || typeof value !== 'object') return false;\n const candidate = value as Partial<SignalProvider<string>> & { __registerMastra?: unknown };\n return (\n typeof candidate.id === 'string' &&\n candidate.id.length > 0 &&\n typeof candidate.connect === 'function' &&\n typeof candidate.startPolling === 'function' &&\n typeof candidate.stop === 'function' &&\n typeof candidate.__registerMastra === 'function'\n );\n}\n\n/** A bare array is shorthand for the input lane, the common case. */\nfunction normalizePluginProcessorEntries(entries: MastraCodePluginProcessorEntries): LoadedPluginProcessors {\n const input = Array.isArray(entries) ? entries : (entries.input ?? []);\n const output = Array.isArray(entries) ? [] : (entries.output ?? []);\n if (!Array.isArray(input) || !Array.isArray(output)) {\n throw new Error('Plugin processor lanes must be arrays');\n }\n for (const processor of [...input, ...output]) {\n if (!processor || typeof processor !== 'object' || typeof processor.id !== 'string') {\n throw new Error('Plugin processors must be objects with an id');\n }\n }\n return { input, output };\n}\n\nasync function resolvePluginInstructions(\n plugin: MastraCodePlugin,\n context: MastraCodePluginContext,\n): Promise<string | undefined> {\n if (plugin.instructions === undefined) return undefined;\n const instructions =\n typeof plugin.instructions === 'function' ? await plugin.instructions(context) : plugin.instructions;\n if (typeof instructions !== 'string') {\n throw new Error('Plugin instructions must be a string');\n }\n const trimmed = instructions.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction normalizePluginToolEntries(entries: MastraCodePluginToolEntries): {\n tools: MastraCodePluginTools;\n renderConfigs: Record<string, MastraCodeToolRenderConfig>;\n} {\n const tools: MastraCodePluginTools = {};\n const renderConfigs: Record<string, MastraCodeToolRenderConfig> = {};\n for (const [name, entry] of Object.entries(entries)) {\n if (!isToolEntryObject(entry)) {\n throw new Error(`Plugin tool \"${name}\" must be an object with a tool property`);\n }\n tools[name] = entry.tool;\n if (entry.render) renderConfigs[name] = entry.render;\n }\n return { tools, renderConfigs };\n}\n\nfunction isToolEntryObject(entry: MastraCodePluginToolEntries[string]): entry is MastraCodePluginToolEntries[string] {\n if (!entry || typeof entry !== 'object' || !('tool' in entry)) return false;\n const tool = (entry as { tool?: unknown }).tool;\n return !!tool && typeof tool === 'object' && !Array.isArray(tool);\n}\n\nfunction validatePluginConfigSchema(schema: unknown): MastraCodePluginConfigSchema | undefined {\n if (!schema || typeof schema !== 'object' || Array.isArray(schema)) return undefined;\n const validated: MastraCodePluginConfigSchema = {};\n for (const [key, option] of Object.entries(schema)) {\n if (!option || typeof option !== 'object' || Array.isArray(option)) continue;\n const record = option as Record<string, unknown>;\n if (record.type !== 'model' && record.type !== 'boolean' && record.type !== 'string') continue;\n validated[key] = {\n type: record.type,\n ...(typeof record.label === 'string' ? { label: record.label } : {}),\n ...(typeof record.description === 'string' ? { description: record.description } : {}),\n ...(typeof record.default === 'string' || typeof record.default === 'boolean' ? { default: record.default } : {}),\n };\n }\n return Object.keys(validated).length > 0 ? validated : undefined;\n}\n\nfunction resolvePluginConfigValues(\n schema: MastraCodePluginConfigSchema | undefined,\n recordValues: Record<string, unknown> | undefined,\n): MastraCodePluginConfigValues {\n const values: MastraCodePluginConfigValues = {};\n if (!schema) return values;\n for (const [key, option] of Object.entries(schema)) {\n const value = recordValues?.[key];\n if (option.type === 'boolean') {\n values[key] = typeof value === 'boolean' ? value : typeof option.default === 'boolean' ? option.default : false;\n continue;\n }\n values[key] = typeof value === 'string' ? value : typeof option.default === 'string' ? option.default : undefined;\n }\n return values;\n}\n\nexport function collectActivePluginTools(plugins: LoadedPlugin[]): MastraCodePluginTools {\n const tools: MastraCodePluginTools = {};\n for (const plugin of plugins) {\n if (plugin.status !== 'active') continue;\n for (const [name, tool] of Object.entries(plugin.tools)) {\n if (!(name in tools)) {\n tools[name] = tool;\n }\n }\n }\n return tools;\n}\n\nfunction markToolConflicts(plugins: LoadedPlugin[]): LoadedPlugin[] {\n const seen = new Map<string, string>();\n return plugins.map(plugin => {\n if (plugin.status !== 'active') return plugin;\n const conflicts = plugin.toolNames.filter(toolName => seen.has(toolName));\n for (const toolName of plugin.toolNames) {\n if (!seen.has(toolName)) seen.set(toolName, plugin.id);\n }\n return conflicts.length > 0 ? { ...plugin, status: 'conflicted', conflicts } : plugin;\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"loader.js","names":[],"sources":["../../src/plugins/loader.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport { isSignalProvider } from '@mastra/core/signals';\nimport type { SignalProvider } from '@mastra/core/signals';\n\nimport type {\n MastraCodePlugin,\n MastraCodePluginConfigSchema,\n MastraCodePluginConfigValues,\n MastraCodePluginContext,\n MastraCodePluginProcessorEntries,\n MastraCodePluginRuntime,\n MastraCodePluginSignalProviderEntries,\n MastraCodePluginToolEntries,\n MastraCodePluginTools,\n MastraCodeToolRenderConfig,\n} from '../plugin.js';\nimport { getPluginRoot } from './paths.js';\nimport type { PluginPathOptions } from './paths.js';\nimport { loadPluginRegistry, mergePluginRegistries } from './registry.js';\nimport type { LoadedPlugin, LoadedPluginProcessors, PluginRegistry, ScopedInstalledPluginRecord } from './types.js';\n\nexport type LoadPluginRecordOptions = PluginPathOptions & {\n /** Lazy accessors for Mastra Code's runtime, passed on to plugin field resolvers. */\n runtime?: MastraCodePluginRuntime;\n};\n\nexport type LoadPluginsOptions = LoadPluginRecordOptions & {\n globalRegistry?: PluginRegistry;\n projectRegistry?: PluginRegistry;\n};\n\nexport async function loadPlugins(options: LoadPluginsOptions): Promise<LoadedPlugin[]> {\n const globalRegistry =\n options.globalRegistry ?? loadPluginRegistry(path.join(getPluginRoot('global', options), 'plugins.json'));\n const projectRegistry =\n options.projectRegistry ?? loadPluginRegistry(path.join(getPluginRoot('project', options), 'plugins.json'));\n const records = mergePluginRegistries(globalRegistry, projectRegistry);\n const loaded: LoadedPlugin[] = [];\n\n for (const record of records) {\n if (record.blocked) {\n loaded.push({ ...record, status: 'blocked', tools: {}, toolNames: [] });\n continue;\n }\n if (!record.enabled) {\n loaded.push({ ...record, status: 'inactive', tools: {}, toolNames: [] });\n continue;\n }\n\n loaded.push(await loadPluginRecord(record, options));\n }\n\n return markToolConflicts(loaded);\n}\n\nexport async function loadPluginRecord(\n record: ScopedInstalledPluginRecord,\n options: LoadPluginRecordOptions,\n): Promise<LoadedPlugin> {\n try {\n const entryPath = resolvePluginEntryPath(record, options);\n const plugin = await importPluginModule(entryPath);\n if (plugin.id !== record.id) {\n throw new Error(`Plugin id mismatch: registry has \"${record.id}\" but module exports \"${plugin.id}\"`);\n }\n\n const configSchema = validatePluginConfigSchema(plugin.config);\n const configValues = resolvePluginConfigValues(configSchema, record.config);\n const pluginDir = path.dirname(entryPath);\n const pluginRoot = resolvePluginRoot(record, options);\n const context: MastraCodePluginContext = {\n cwd: options.projectRoot,\n scope: record.scope,\n pluginDir,\n config: configValues,\n // Accessors, not instances: plugins load before the controller and the\n // session exist, so a plugin resolves these when it needs them, not now.\n getController: options.runtime?.getController,\n getActiveSession: options.runtime?.getActiveSession,\n getStorage: options.runtime?.getStorage,\n };\n const { tools, renderConfigs } = await resolvePluginTools(plugin, context);\n const processors = await resolvePluginProcessors(plugin, context);\n const signalProviders = await resolvePluginSignalProviders(plugin, context);\n const instructions = await resolvePluginInstructions(plugin, context);\n\n return {\n ...record,\n name: plugin.name,\n version: plugin.version ?? record.version,\n description: plugin.description,\n instructions,\n status: 'active',\n tools,\n renderConfigs,\n toolNames: Object.keys(tools).sort(),\n processors,\n signalProviders,\n skillPaths: resolveExistingAssetDirs(pluginRoot, 'skills'),\n commandPaths: resolveExistingAssetDirs(pluginRoot, 'commands'),\n configSchema,\n configValues,\n };\n } catch (error) {\n return {\n ...record,\n status: 'load failed',\n error: error instanceof Error ? error.message : String(error),\n tools: {},\n toolNames: [],\n };\n }\n}\n\nexport async function loadPluginFromEntry(entryPath: string): Promise<MastraCodePlugin> {\n return validatePluginExport(await importPluginModule(entryPath));\n}\n\nexport function resolvePluginRoot(record: ScopedInstalledPluginRecord, options: PluginPathOptions): string {\n const scopeRoot = path.resolve(getPluginRoot(record.scope, options));\n const pluginRoot = path.resolve(path.isAbsolute(record.path) ? record.path : path.join(scopeRoot, record.path));\n if (record.source === 'github' && !isInsideDirectory(pluginRoot, scopeRoot)) {\n throw new Error(`Plugin path for \"${record.id}\" must be inside the ${record.scope} plugin directory`);\n }\n return pluginRoot;\n}\n\nexport function resolvePluginEntryPath(record: ScopedInstalledPluginRecord, options: PluginPathOptions): string {\n const pluginRoot = resolvePluginRoot(record, options);\n const entryPath = path.resolve(pluginRoot, record.entry);\n if (!isInsideDirectory(entryPath, pluginRoot)) {\n throw new Error(`Plugin entry for \"${record.id}\" must be inside the plugin directory`);\n }\n return entryPath;\n}\n\nexport function isInsideDirectory(targetPath: string, root: string): boolean {\n const resolvedTarget = path.resolve(targetPath);\n const resolvedRoot = path.resolve(root);\n return resolvedTarget === resolvedRoot || resolvedTarget.startsWith(resolvedRoot + path.sep);\n}\n\nfunction resolveExistingAssetDirs(pluginRoot: string, dirname: 'skills' | 'commands'): string[] {\n const dir = path.join(pluginRoot, dirname);\n try {\n return fs.statSync(dir).isDirectory() ? [dir] : [];\n } catch {\n return [];\n }\n}\n\nasync function importPluginModule(entryPath: string): Promise<MastraCodePlugin> {\n if (path.extname(entryPath) !== '.ts') {\n throw new Error(\n `Unsupported plugin entry extension \"${path.extname(entryPath)}\". V1 plugins must use .ts entries.`,\n );\n }\n\n const url = pathToFileURL(entryPath);\n const contentHash = Buffer.from(await globalThis.crypto.subtle.digest('SHA-1', fs.readFileSync(entryPath))).toString(\n 'hex',\n );\n const stat = fs.statSync(entryPath, { bigint: true });\n url.searchParams.set('contentHash', contentHash);\n url.searchParams.set('mtimeNs', stat.mtimeNs.toString());\n const mod = (await import(url.href)) as { default?: unknown; plugin?: unknown };\n return validatePluginExport(mod.default ?? mod.plugin);\n}\n\nfunction validatePluginExport(value: unknown): MastraCodePlugin {\n if (!value || typeof value !== 'object') {\n throw new Error('Plugin module must export a plugin object as default or named \"plugin\" export');\n }\n\n const plugin = value as MastraCodePlugin;\n if (typeof plugin.id !== 'string' || plugin.id.trim().length === 0) {\n throw new Error('Plugin id must be a non-empty string');\n }\n\n if (plugin.tools !== undefined && typeof plugin.tools !== 'object' && typeof plugin.tools !== 'function') {\n throw new Error('Plugin tools must be an object or function');\n }\n\n if (\n plugin.processors !== undefined &&\n typeof plugin.processors !== 'object' &&\n typeof plugin.processors !== 'function'\n ) {\n throw new Error('Plugin processors must be an array, object, or function');\n }\n\n if (\n plugin.signalProviders !== undefined &&\n !Array.isArray(plugin.signalProviders) &&\n typeof plugin.signalProviders !== 'function'\n ) {\n throw new Error('Plugin signal providers must be an array or function');\n }\n\n return plugin;\n}\n\nasync function resolvePluginTools(\n plugin: MastraCodePlugin,\n context: MastraCodePluginContext,\n): Promise<{ tools: MastraCodePluginTools; renderConfigs: Record<string, MastraCodeToolRenderConfig> }> {\n if (!plugin.tools) return { tools: {}, renderConfigs: {} };\n const entries = typeof plugin.tools === 'function' ? await plugin.tools(context) : plugin.tools;\n if (!entries || typeof entries !== 'object' || Array.isArray(entries)) {\n throw new Error('Plugin tools function must return an object');\n }\n return normalizePluginToolEntries(entries);\n}\n\n/** Mirrors {@link resolvePluginTools}: object-or-function, resolved with the same context. */\nasync function resolvePluginProcessors(\n plugin: MastraCodePlugin,\n context: MastraCodePluginContext,\n): Promise<LoadedPluginProcessors> {\n if (!plugin.processors) return { input: [], output: [] };\n const entries = typeof plugin.processors === 'function' ? await plugin.processors(context) : plugin.processors;\n if (!entries || typeof entries !== 'object') {\n throw new Error('Plugin processors function must return an array or object');\n }\n return normalizePluginProcessorEntries(entries);\n}\n\nasync function resolvePluginSignalProviders(\n plugin: MastraCodePlugin,\n context: MastraCodePluginContext,\n): Promise<MastraCodePluginSignalProviderEntries> {\n if (!plugin.signalProviders) return [];\n const entries =\n typeof plugin.signalProviders === 'function' ? await plugin.signalProviders(context) : plugin.signalProviders;\n if (!Array.isArray(entries)) {\n throw new Error('Plugin signal providers function must return an array');\n }\n for (const [index, provider] of entries.entries()) {\n if (!isPluginSignalProvider(provider)) {\n throw new Error(\n `Plugin signal provider at index ${index} must be a SignalProvider (an object with an id that implements connect, startPolling, stop and __registerMastra)`,\n );\n }\n }\n return entries;\n}\n\n/**\n * Structural, not `instanceof`.\n *\n * A plugin that depends on a published provider package — the motivating case, a plugin wrapping\n * `@mastra/github-signals` — installs that package's own copy of `@mastra/core`, so its provider is\n * never an instance of the `SignalProvider` class Mastra Code loaded. `isSignalProvider` would reject\n * a perfectly working provider. Nothing in the lifecycle needs class identity: the lane only calls\n * these methods, so the methods are what is checked.\n */\nfunction isPluginSignalProvider(value: unknown): value is SignalProvider<string> {\n if (isSignalProvider(value)) return true;\n if (!value || typeof value !== 'object') return false;\n const candidate = value as Partial<SignalProvider<string>> & { __registerMastra?: unknown };\n return (\n typeof candidate.id === 'string' &&\n candidate.id.length > 0 &&\n typeof candidate.connect === 'function' &&\n typeof candidate.startPolling === 'function' &&\n typeof candidate.stop === 'function' &&\n typeof candidate.__registerMastra === 'function'\n );\n}\n\n/** A bare array is shorthand for the input lane, the common case. */\nfunction normalizePluginProcessorEntries(entries: MastraCodePluginProcessorEntries): LoadedPluginProcessors {\n const input = Array.isArray(entries) ? entries : (entries.input ?? []);\n const output = Array.isArray(entries) ? [] : (entries.output ?? []);\n if (!Array.isArray(input) || !Array.isArray(output)) {\n throw new Error('Plugin processor lanes must be arrays');\n }\n for (const processor of [...input, ...output]) {\n if (!processor || typeof processor !== 'object' || typeof processor.id !== 'string') {\n throw new Error('Plugin processors must be objects with an id');\n }\n }\n return { input, output };\n}\n\nasync function resolvePluginInstructions(\n plugin: MastraCodePlugin,\n context: MastraCodePluginContext,\n): Promise<string | undefined> {\n if (plugin.instructions === undefined) return undefined;\n const instructions =\n typeof plugin.instructions === 'function' ? await plugin.instructions(context) : plugin.instructions;\n if (typeof instructions !== 'string') {\n throw new Error('Plugin instructions must be a string');\n }\n const trimmed = instructions.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction normalizePluginToolEntries(entries: MastraCodePluginToolEntries): {\n tools: MastraCodePluginTools;\n renderConfigs: Record<string, MastraCodeToolRenderConfig>;\n} {\n const tools: MastraCodePluginTools = {};\n const renderConfigs: Record<string, MastraCodeToolRenderConfig> = {};\n for (const [name, entry] of Object.entries(entries)) {\n if (!isToolEntryObject(entry)) {\n throw new Error(`Plugin tool \"${name}\" must be an object with a tool property`);\n }\n tools[name] = entry.tool;\n if (entry.render) renderConfigs[name] = entry.render;\n }\n return { tools, renderConfigs };\n}\n\nfunction isToolEntryObject(entry: MastraCodePluginToolEntries[string]): entry is MastraCodePluginToolEntries[string] {\n if (!entry || typeof entry !== 'object' || !('tool' in entry)) return false;\n const tool = (entry as { tool?: unknown }).tool;\n return !!tool && typeof tool === 'object' && !Array.isArray(tool);\n}\n\nfunction validatePluginConfigSchema(schema: unknown): MastraCodePluginConfigSchema | undefined {\n if (!schema || typeof schema !== 'object' || Array.isArray(schema)) return undefined;\n const validated: MastraCodePluginConfigSchema = {};\n for (const [key, option] of Object.entries(schema)) {\n if (!option || typeof option !== 'object' || Array.isArray(option)) continue;\n const record = option as Record<string, unknown>;\n if (record.type !== 'model' && record.type !== 'boolean' && record.type !== 'string') continue;\n validated[key] = {\n type: record.type,\n ...(typeof record.label === 'string' ? { label: record.label } : {}),\n ...(typeof record.description === 'string' ? { description: record.description } : {}),\n ...(typeof record.default === 'string' || typeof record.default === 'boolean' ? { default: record.default } : {}),\n };\n }\n return Object.keys(validated).length > 0 ? validated : undefined;\n}\n\nfunction resolvePluginConfigValues(\n schema: MastraCodePluginConfigSchema | undefined,\n recordValues: Record<string, unknown> | undefined,\n): MastraCodePluginConfigValues {\n const values: MastraCodePluginConfigValues = {};\n if (!schema) return values;\n for (const [key, option] of Object.entries(schema)) {\n const value = recordValues?.[key];\n if (option.type === 'boolean') {\n values[key] = typeof value === 'boolean' ? value : typeof option.default === 'boolean' ? option.default : false;\n continue;\n }\n values[key] = typeof value === 'string' ? value : typeof option.default === 'string' ? option.default : undefined;\n }\n return values;\n}\n\nexport function collectActivePluginTools(plugins: LoadedPlugin[]): MastraCodePluginTools {\n const tools: MastraCodePluginTools = {};\n for (const plugin of plugins) {\n if (plugin.status !== 'active') continue;\n for (const [name, tool] of Object.entries(plugin.tools)) {\n if (!(name in tools)) {\n tools[name] = tool;\n }\n }\n }\n return tools;\n}\n\nfunction markToolConflicts(plugins: LoadedPlugin[]): LoadedPlugin[] {\n const seen = new Map<string, string>();\n return plugins.map(plugin => {\n if (plugin.status !== 'active') return plugin;\n const conflicts = plugin.toolNames.filter(toolName => seen.has(toolName));\n for (const toolName of plugin.toolNames) {\n if (!seen.has(toolName)) seen.set(toolName, plugin.id);\n }\n return conflicts.length > 0 ? { ...plugin, status: 'conflicted', conflicts } : plugin;\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-memory-diagnostics.d.ts","sourceRoot":"","sources":["../src/process-memory-diagnostics.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"process-memory-diagnostics.d.ts","sourceRoot":"","sources":["../src/process-memory-diagnostics.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { join } from "path";
|
|
2
2
|
import { homedir } from "os";
|
|
3
|
-
import { randomUUID } from "crypto";
|
|
4
3
|
import { appendFile, chmod, mkdir, rename, rm, writeFile } from "fs/promises";
|
|
4
|
+
import { getHeapSpaceStatistics, getHeapStatistics } from "v8";
|
|
5
5
|
import { Session } from "inspector/promises";
|
|
6
6
|
import { PerformanceObserver } from "perf_hooks";
|
|
7
7
|
import { arch as arch$1, platform as platform$1 } from "process";
|
|
8
|
-
import { getHeapSpaceStatistics, getHeapStatistics } from "v8";
|
|
9
8
|
//#region src/process-memory-diagnostics.ts
|
|
10
9
|
const PROCESS_MEMORY_DIAGNOSTICS_DEFAULTS = {
|
|
11
10
|
sampleIntervalMs: 1e4,
|
|
@@ -156,7 +155,7 @@ var ProcessMemoryDiagnostics = class {
|
|
|
156
155
|
this.createInspectorSession = dependencies.createInspectorSession ?? defaultInspectorSession;
|
|
157
156
|
this.createPerformanceObserver = dependencies.createPerformanceObserver ?? defaultPerformanceObserver;
|
|
158
157
|
this.now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
|
|
159
|
-
this.randomId = dependencies.randomId ?? randomUUID;
|
|
158
|
+
this.randomId = dependencies.randomId ?? (() => globalThis.crypto.randomUUID());
|
|
160
159
|
}
|
|
161
160
|
getStatus() {
|
|
162
161
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-memory-diagnostics.js","names":["platform"],"sources":["../src/process-memory-diagnostics.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { appendFile, chmod, mkdir, rename, rm, writeFile } from 'node:fs/promises';\nimport { Session } from 'node:inspector/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { PerformanceObserver, type PerformanceEntry } from 'node:perf_hooks';\nimport { arch, platform } from 'node:process';\nimport { getHeapSpaceStatistics, getHeapStatistics } from 'node:v8';\n\nexport const PROCESS_MEMORY_DIAGNOSTICS_DEFAULTS = {\n sampleIntervalMs: 10_000,\n captureIntervalMs: 300_000,\n allocationIntervalBytes: 524_288,\n} as const;\n\nexport const PROCESS_MEMORY_DIAGNOSTICS_MINIMUMS = {\n sampleIntervalMs: 1_000,\n captureIntervalMs: 10_000,\n allocationIntervalBytes: 32_768,\n} as const;\n\nconst MAX_TIMER_DELAY_MS = 2_147_483_647;\nconst MAX_PENDING_GC_EVENTS = 1_000;\n\nexport interface ProcessMemoryDiagnosticsConfig {\n parentDirectory: string;\n sampleIntervalMs: number;\n captureIntervalMs: number;\n allocationIntervalBytes: number;\n}\n\nexport interface ProcessMemoryDiagnosticsEnvironment {\n MASTRACODE_PROFILE?: string;\n MASTRACODE_PROFILE_DIR?: string;\n MASTRACODE_PROFILE_SAMPLE_INTERVAL_MS?: string;\n MASTRACODE_PROFILE_CAPTURE_INTERVAL_MS?: string;\n MASTRACODE_PROFILE_ALLOCATION_INTERVAL_BYTES?: string;\n}\n\nexport interface ProcessMemoryDiagnosticsMemorySample {\n timestamp: string;\n sequence: number;\n elapsedMs: number;\n memory: ReturnType<typeof process.memoryUsage>;\n resourceUsage: ReturnType<typeof process.resourceUsage>;\n heap: ReturnType<typeof getHeapStatistics>;\n heapSpaces: ReturnType<typeof getHeapSpaceStatistics>;\n}\n\nexport interface ProcessMemoryDiagnosticsStatus {\n state: 'inactive' | 'starting' | 'active' | 'stopping' | 'error';\n outputDirectory: string | null;\n config: ProcessMemoryDiagnosticsConfig;\n sampleCount: number;\n captureCount: number;\n gcEventCount: number;\n latestSample: ProcessMemoryDiagnosticsMemorySample | null;\n latestCapturePath: string | null;\n error: string | null;\n}\n\nexport interface ProcessMemoryDiagnosticsCapture {\n path: string;\n sequence: number;\n timestamp: string;\n reason: 'manual' | 'periodic' | 'stop';\n}\n\ninterface InspectorSessionAdapter {\n connect(): void;\n disconnect(): void;\n post(method: string, params?: Record<string, unknown>): Promise<Record<string, unknown>>;\n}\n\ninterface PerformanceObserverAdapter {\n observe(options: Parameters<PerformanceObserver['observe']>[0]): void;\n disconnect(): void;\n}\n\ninterface ProcessMemoryDiagnosticsDependencies {\n createInspectorSession?: () => InspectorSessionAdapter;\n createPerformanceObserver?: (callback: (entries: PerformanceEntry[]) => void) => PerformanceObserverAdapter;\n now?: () => Date;\n randomId?: () => string;\n}\n\nexport interface ProcessMemoryDiagnosticsSetup {\n diagnostics: ProcessMemoryDiagnostics;\n enabled: boolean;\n error: string | null;\n}\n\nexport class ProcessMemoryDiagnosticsConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ProcessMemoryDiagnosticsConfigError';\n }\n}\n\nfunction isEnabled(value: string | undefined): boolean {\n return ['1', 'true', 'yes', 'on'].includes(value?.trim().toLowerCase() ?? '');\n}\n\nfunction parseBoundedInteger(\n env: ProcessMemoryDiagnosticsEnvironment,\n name: keyof ProcessMemoryDiagnosticsEnvironment,\n fallback: number,\n minimum: number,\n maximum?: number,\n): number {\n const raw = env[name];\n if (raw === undefined || raw.trim() === '') return fallback;\n const value = Number(raw);\n if (!Number.isSafeInteger(value) || value < minimum || (maximum !== undefined && value > maximum)) {\n const range = maximum === undefined ? `greater than or equal to ${minimum}` : `between ${minimum} and ${maximum}`;\n throw new ProcessMemoryDiagnosticsConfigError(\n `${name} must be an integer ${range}; received ${JSON.stringify(raw)}.`,\n );\n }\n return value;\n}\n\nfunction getDefaultProfileParentDirectory(): string {\n if (process.env.MASTRA_APP_DATA_DIR) return join(process.env.MASTRA_APP_DATA_DIR, 'profiles');\n\n const baseDirectory =\n platform === 'darwin'\n ? join(homedir(), 'Library', 'Application Support')\n : platform === 'win32'\n ? process.env.APPDATA || join(homedir(), 'AppData', 'Roaming')\n : process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share');\n return join(baseDirectory, 'mastracode', 'profiles');\n}\n\nexport function parseProcessMemoryDiagnosticsEnvironment(env: ProcessMemoryDiagnosticsEnvironment = process.env): {\n enabled: boolean;\n config: ProcessMemoryDiagnosticsConfig;\n} {\n return {\n enabled: isEnabled(env.MASTRACODE_PROFILE),\n config: {\n parentDirectory: env.MASTRACODE_PROFILE_DIR?.trim() || getDefaultProfileParentDirectory(),\n sampleIntervalMs: parseBoundedInteger(\n env,\n 'MASTRACODE_PROFILE_SAMPLE_INTERVAL_MS',\n PROCESS_MEMORY_DIAGNOSTICS_DEFAULTS.sampleIntervalMs,\n PROCESS_MEMORY_DIAGNOSTICS_MINIMUMS.sampleIntervalMs,\n MAX_TIMER_DELAY_MS,\n ),\n captureIntervalMs: parseBoundedInteger(\n env,\n 'MASTRACODE_PROFILE_CAPTURE_INTERVAL_MS',\n PROCESS_MEMORY_DIAGNOSTICS_DEFAULTS.captureIntervalMs,\n PROCESS_MEMORY_DIAGNOSTICS_MINIMUMS.captureIntervalMs,\n MAX_TIMER_DELAY_MS,\n ),\n allocationIntervalBytes: parseBoundedInteger(\n env,\n 'MASTRACODE_PROFILE_ALLOCATION_INTERVAL_BYTES',\n PROCESS_MEMORY_DIAGNOSTICS_DEFAULTS.allocationIntervalBytes,\n PROCESS_MEMORY_DIAGNOSTICS_MINIMUMS.allocationIntervalBytes,\n ),\n },\n };\n}\n\nexport function createProcessMemoryDiagnosticsFromEnvironment(\n env: ProcessMemoryDiagnosticsEnvironment = process.env,\n dependencies: ProcessMemoryDiagnosticsDependencies = {},\n): ProcessMemoryDiagnosticsSetup {\n try {\n const { enabled, config } = parseProcessMemoryDiagnosticsEnvironment(env);\n return { diagnostics: new ProcessMemoryDiagnostics(config, dependencies), enabled, error: null };\n } catch (error) {\n const config: ProcessMemoryDiagnosticsConfig = {\n parentDirectory: env.MASTRACODE_PROFILE_DIR?.trim() || getDefaultProfileParentDirectory(),\n ...PROCESS_MEMORY_DIAGNOSTICS_DEFAULTS,\n };\n const message = error instanceof Error ? error.message : String(error);\n return {\n diagnostics: new ProcessMemoryDiagnostics(config, dependencies, message),\n enabled: isEnabled(env.MASTRACODE_PROFILE),\n error: message,\n };\n }\n}\n\nexport async function startConfiguredProcessMemoryDiagnostics(\n setup: ProcessMemoryDiagnosticsSetup,\n warn: (message: string) => void,\n): Promise<ProcessMemoryDiagnostics> {\n if (!setup.enabled) return setup.diagnostics;\n if (setup.error) {\n warn(`Process memory diagnostics were not started: ${setup.error}`);\n return setup.diagnostics;\n }\n\n const status = await setup.diagnostics.start();\n if (status.state !== 'active') {\n warn(`Process memory diagnostics were not started: ${status.error ?? 'unknown inspector error'}`);\n }\n return setup.diagnostics;\n}\n\nexport async function stopProcessMemoryDiagnosticsWithTimeout(\n diagnostics: ProcessMemoryDiagnostics,\n warn: (message: string) => void,\n timeoutMs = 5_000,\n): Promise<void> {\n let timeout: ReturnType<typeof setTimeout> | undefined;\n const timedOut = await Promise.race([\n diagnostics.stop().then(\n () => false,\n error => {\n warn(`Process memory diagnostics did not stop cleanly: ${errorMessage(error)}`);\n return false;\n },\n ),\n new Promise<true>(resolve => {\n timeout = setTimeout(() => resolve(true), timeoutMs);\n timeout.unref();\n }),\n ]);\n if (timeout) clearTimeout(timeout);\n if (timedOut)\n warn(`Process memory diagnostics did not stop within ${timeoutMs}ms; final artifacts may be incomplete.`);\n}\n\nfunction defaultInspectorSession(): InspectorSessionAdapter {\n const session = new Session();\n return {\n connect: () => session.connect(),\n disconnect: () => session.disconnect(),\n post: async (method, params) =>\n (await session.post(\n method as Parameters<Session['post']>[0],\n params as Parameters<Session['post']>[1],\n )) as unknown as Record<string, unknown>,\n };\n}\n\nfunction defaultPerformanceObserver(callback: (entries: PerformanceEntry[]) => void) {\n const observer = new PerformanceObserver(list => callback(list.getEntries()));\n return observer;\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction safeTimestamp(date: Date): string {\n return date.toISOString().replaceAll(':', '-');\n}\n\nfunction unrefTimer(timer: ReturnType<typeof setInterval>): void {\n timer.unref?.();\n}\n\nexport class ProcessMemoryDiagnostics {\n readonly config: ProcessMemoryDiagnosticsConfig;\n\n private state: ProcessMemoryDiagnosticsStatus['state'] = 'inactive';\n private outputDirectory: string | null = null;\n private inspector: InspectorSessionAdapter | null = null;\n private observer: PerformanceObserverAdapter | null = null;\n private sampleTimer: ReturnType<typeof setInterval> | null = null;\n private captureTimer: ReturnType<typeof setInterval> | null = null;\n private startedAt: Date | null = null;\n private sampleCount = 0;\n private captureCount = 0;\n private gcEventCount = 0;\n private latestSample: ProcessMemoryDiagnosticsMemorySample | null = null;\n private latestCapturePath: string | null = null;\n private latestError: string | null;\n private samplingActive = false;\n private stopRequested = false;\n private startingPromise: Promise<ProcessMemoryDiagnosticsStatus> | null = null;\n private stoppingPromise: Promise<ProcessMemoryDiagnosticsStatus> | null = null;\n private restartAfterStopPromise: Promise<ProcessMemoryDiagnosticsStatus> | null = null;\n private restartRequested = false;\n private pendingGcEvents: Array<Record<string, unknown>> = [];\n private gcEventBufferOverflowed = false;\n private artifactWriteFailed = false;\n private captureQueue: Promise<unknown> = Promise.resolve();\n private writeQueue: Promise<unknown> = Promise.resolve();\n\n private readonly configError: string | null;\n private readonly createInspectorSession: () => InspectorSessionAdapter;\n private readonly createPerformanceObserver: NonNullable<\n ProcessMemoryDiagnosticsDependencies['createPerformanceObserver']\n >;\n private readonly now: () => Date;\n private readonly randomId: () => string;\n\n constructor(\n config: ProcessMemoryDiagnosticsConfig,\n dependencies: ProcessMemoryDiagnosticsDependencies = {},\n initialError: string | null = null,\n ) {\n this.config = { ...config };\n this.configError = initialError;\n this.latestError = initialError;\n this.createInspectorSession = dependencies.createInspectorSession ?? defaultInspectorSession;\n this.createPerformanceObserver = dependencies.createPerformanceObserver ?? defaultPerformanceObserver;\n this.now = dependencies.now ?? (() => new Date());\n this.randomId = dependencies.randomId ?? randomUUID;\n }\n\n getStatus(): ProcessMemoryDiagnosticsStatus {\n return {\n state: this.state,\n outputDirectory: this.outputDirectory,\n config: { ...this.config },\n sampleCount: this.sampleCount,\n captureCount: this.captureCount,\n gcEventCount: this.gcEventCount,\n latestSample: this.latestSample,\n latestCapturePath: this.latestCapturePath,\n error: this.latestError,\n };\n }\n\n start(): Promise<ProcessMemoryDiagnosticsStatus> {\n if (this.state === 'active') return Promise.resolve(this.getStatus());\n if (this.state === 'starting' && this.startingPromise) return this.startingPromise;\n if (this.state === 'stopping' && this.stoppingPromise) {\n this.restartRequested = true;\n this.restartAfterStopPromise ??= this.stoppingPromise.then(() => {\n this.restartAfterStopPromise = null;\n if (!this.restartRequested) return this.getStatus();\n this.restartRequested = false;\n return this.start();\n });\n return this.restartAfterStopPromise;\n }\n if (this.configError) {\n this.latestError = this.configError;\n this.state = 'error';\n return Promise.resolve(this.getStatus());\n }\n\n this.state = 'starting';\n this.stoppingPromise = null;\n this.restartRequested = false;\n this.stopRequested = false;\n this.outputDirectory = null;\n this.sampleCount = 0;\n this.captureCount = 0;\n this.gcEventCount = 0;\n this.pendingGcEvents = [];\n this.gcEventBufferOverflowed = false;\n this.artifactWriteFailed = false;\n this.latestSample = null;\n this.latestCapturePath = null;\n this.latestError = null;\n this.startedAt = this.now();\n\n const startingPromise = this.startRun();\n this.startingPromise = startingPromise;\n void startingPromise.finally(() => {\n if (this.startingPromise === startingPromise) this.startingPromise = null;\n });\n return startingPromise;\n }\n\n private async startRun(): Promise<ProcessMemoryDiagnosticsStatus> {\n try {\n await this.createArtifacts();\n if (await this.abortStartIfRequested()) return this.getStatus();\n\n this.observeGc();\n this.inspector = this.createInspectorSession();\n this.inspector.connect();\n await this.startSampling();\n if (await this.abortStartIfRequested()) return this.getStatus();\n\n await this.takeSample();\n if (await this.abortStartIfRequested()) return this.getStatus();\n\n this.sampleTimer = setInterval(() => {\n void this.takeSample().catch(error => this.recordError('Process sample failed', error));\n }, this.config.sampleIntervalMs);\n unrefTimer(this.sampleTimer);\n\n this.captureTimer = setInterval(() => {\n void this.capture('periodic').catch(error => this.recordError('Periodic allocation capture failed', error));\n }, this.config.captureIntervalMs);\n unrefTimer(this.captureTimer);\n\n this.state = 'active';\n return this.getStatus();\n } catch (error) {\n this.latestError = `Unable to start process memory diagnostics: ${errorMessage(error)}`;\n await this.cleanupAfterStartFailure();\n this.state = 'error';\n return this.getStatus();\n }\n }\n\n private async abortStartIfRequested(): Promise<boolean> {\n if (!this.stopRequested) return false;\n await this.cleanupAfterStartFailure();\n this.state = 'inactive';\n return true;\n }\n\n capture(reason: 'manual' | 'periodic' = 'manual'): Promise<ProcessMemoryDiagnosticsCapture> {\n if (this.state !== 'active') {\n return Promise.reject(new Error(this.latestError ?? 'Process memory diagnostics are not active.'));\n }\n return this.enqueueCapture(() => this.captureEpoch(reason, false));\n }\n\n async stop(): Promise<ProcessMemoryDiagnosticsStatus> {\n if (this.stoppingPromise) {\n this.restartRequested = false;\n return this.stoppingPromise;\n }\n if (this.state === 'inactive') return this.getStatus();\n if (this.state === 'error' && !this.inspector && !this.outputDirectory) return this.getStatus();\n\n const startingPromise = this.state === 'starting' ? this.startingPromise : null;\n this.stopRequested = true;\n this.state = 'stopping';\n this.clearTimersAndObserver();\n\n if (startingPromise) {\n this.stoppingPromise = startingPromise.then(() => this.getStatus());\n return this.stoppingPromise;\n }\n\n this.stoppingPromise = (async () => {\n let stopError: string | null = null;\n try {\n if (this.outputDirectory) await this.takeSample();\n await this.enqueueCapture(async () => {\n if (this.inspector && this.samplingActive) await this.captureEpoch('stop', true);\n });\n await this.writeQueue;\n if (this.gcEventBufferOverflowed) {\n throw new Error(`GC event buffer exceeded ${MAX_PENDING_GC_EVENTS} records before the next process sample.`);\n }\n if (this.artifactWriteFailed) throw new Error('One or more process memory diagnostic artifact writes failed.');\n this.latestError = null;\n } catch (error) {\n stopError = `Unable to stop process memory diagnostics cleanly: ${errorMessage(error)}`;\n this.latestError = stopError;\n } finally {\n this.disconnectInspector();\n this.state = stopError ? 'error' : 'inactive';\n }\n return this.getStatus();\n })();\n\n return this.stoppingPromise;\n }\n\n private async createArtifacts(): Promise<void> {\n await mkdir(this.config.parentDirectory, { recursive: true, mode: 0o700 });\n await chmod(this.config.parentDirectory, 0o700);\n const runName = `run-${safeTimestamp(this.startedAt!)}-${process.pid}-${this.randomId()\n .replaceAll(/[^a-zA-Z0-9-]/g, '')\n .slice(0, 12)}`;\n this.outputDirectory = join(this.config.parentDirectory, runName);\n await mkdir(this.outputDirectory, { mode: 0o700 });\n await chmod(this.outputDirectory, 0o700);\n\n const metadata = {\n schemaVersion: 1,\n runId: runName,\n startedAt: this.startedAt!.toISOString(),\n pid: process.pid,\n nodeVersion: process.version,\n platform,\n arch,\n config: this.config,\n };\n await writeFile(join(this.outputDirectory, 'metadata.json'), `${JSON.stringify(metadata, null, 2)}\\n`, {\n flag: 'wx',\n mode: 0o600,\n });\n await writeFile(join(this.outputDirectory, 'process-samples.jsonl'), '', { flag: 'wx', mode: 0o600 });\n await writeFile(join(this.outputDirectory, 'gc-events.jsonl'), '', { flag: 'wx', mode: 0o600 });\n }\n\n private observeGc(): void {\n const observer = this.createPerformanceObserver(entries => {\n const before = this.latestSample?.memory ?? null;\n const after = process.memoryUsage();\n const timestamp = this.now().toISOString();\n for (const entry of entries) {\n const gcEntry = entry as PerformanceEntry & {\n kind?: number;\n flags?: number;\n detail?: { kind?: number; flags?: number };\n };\n this.gcEventCount += 1;\n if (this.pendingGcEvents.length >= MAX_PENDING_GC_EVENTS) {\n this.gcEventBufferOverflowed = true;\n this.latestError = `GC event buffer exceeded ${MAX_PENDING_GC_EVENTS} records before the next process sample.`;\n continue;\n }\n this.pendingGcEvents.push({\n timestamp,\n sequence: this.gcEventCount,\n name: entry.name,\n startTime: entry.startTime,\n duration: entry.duration,\n kind: gcEntry.detail?.kind ?? gcEntry.kind ?? null,\n flags: gcEntry.detail?.flags ?? gcEntry.flags ?? null,\n before,\n after,\n latestSampleSequence: this.latestSample?.sequence ?? null,\n });\n }\n });\n observer.observe({ entryTypes: ['gc'] });\n this.observer = observer;\n }\n\n private async startSampling(): Promise<void> {\n if (!this.inspector) throw new Error('Inspector session is unavailable.');\n await this.inspector.post('HeapProfiler.startSampling', {\n samplingInterval: this.config.allocationIntervalBytes,\n includeObjectsCollectedByMajorGC: true,\n includeObjectsCollectedByMinorGC: true,\n });\n this.samplingActive = true;\n }\n\n private async takeSample(): Promise<ProcessMemoryDiagnosticsMemorySample> {\n if (!this.outputDirectory || !this.startedAt) throw new Error('Diagnostics output directory is unavailable.');\n const sample: ProcessMemoryDiagnosticsMemorySample = {\n timestamp: this.now().toISOString(),\n sequence: this.sampleCount + 1,\n elapsedMs: Math.max(0, this.now().getTime() - this.startedAt.getTime()),\n memory: process.memoryUsage(),\n resourceUsage: process.resourceUsage(),\n heap: getHeapStatistics(),\n heapSpaces: getHeapSpaceStatistics(),\n };\n this.sampleCount = sample.sequence;\n this.latestSample = sample;\n await this.enqueueWrite('process-samples.jsonl', sample);\n const gcEvents = this.pendingGcEvents.splice(0);\n if (gcEvents.length > 0) await this.enqueueWriteBatch('gc-events.jsonl', gcEvents);\n return sample;\n }\n\n private enqueueWrite(fileName: string, value: unknown): Promise<void> {\n return this.enqueueWriteBatch(fileName, [value]);\n }\n\n private enqueueWriteBatch(fileName: string, values: unknown[]): Promise<void> {\n const operation = this.writeQueue.then(async () => {\n if (!this.outputDirectory || values.length === 0) return;\n const lines = values.map(value => JSON.stringify(value)).join('\\n');\n try {\n await appendFile(join(this.outputDirectory, fileName), `${lines}\\n`, { mode: 0o600 });\n } catch (error) {\n this.artifactWriteFailed = true;\n throw error;\n }\n });\n this.writeQueue = operation.catch(() => undefined);\n return operation;\n }\n\n private enqueueCapture<T>(operation: () => Promise<T>): Promise<T> {\n const result = this.captureQueue.then(operation);\n this.captureQueue = result.catch(() => undefined);\n return result;\n }\n\n private async captureEpoch(\n reason: ProcessMemoryDiagnosticsCapture['reason'],\n final: boolean,\n ): Promise<ProcessMemoryDiagnosticsCapture> {\n if (!this.inspector || !this.samplingActive || !this.outputDirectory) {\n throw new Error(this.latestError ?? 'Allocation sampling is not active.');\n }\n\n let response: Record<string, unknown>;\n try {\n response = await this.inspector.post('HeapProfiler.stopSampling');\n this.samplingActive = false;\n } catch (error) {\n this.latestError = `Allocation capture failed: ${errorMessage(error)}`;\n if (!final && !this.stopRequested) await this.restartSamplingAfterFailure();\n throw new Error(this.latestError, { cause: error });\n }\n\n const profile = response.profile;\n if (!profile || typeof profile !== 'object') {\n this.latestError = 'Allocation capture failed: inspector returned no profile.';\n if (!final && !this.stopRequested) await this.restartSamplingAfterFailure();\n throw new Error(this.latestError);\n }\n\n const sequence = this.captureCount + 1;\n const timestamp = this.now().toISOString();\n const filename = `allocation-${String(sequence).padStart(6, '0')}-${safeTimestamp(new Date(timestamp))}.heapprofile`;\n const finalPath = join(this.outputDirectory, filename);\n const temporaryPath = join(this.outputDirectory, `.${filename}.${this.randomId()}.tmp`);\n\n try {\n await writeFile(temporaryPath, `${JSON.stringify(profile)}\\n`, { flag: 'wx', mode: 0o600 });\n await chmod(temporaryPath, 0o600);\n await rename(temporaryPath, finalPath);\n await chmod(finalPath, 0o600);\n this.captureCount = sequence;\n this.latestCapturePath = finalPath;\n this.latestError = null;\n } catch (error) {\n this.artifactWriteFailed = true;\n this.latestError = `Unable to persist allocation capture: ${errorMessage(error)}`;\n throw new Error(this.latestError, { cause: error });\n } finally {\n if (!final && !this.stopRequested && this.state === 'active') {\n try {\n await this.startSampling();\n } catch (error) {\n this.latestError = `Unable to restart allocation sampling: ${errorMessage(error)}`;\n this.state = 'error';\n }\n }\n }\n\n if (!final && !this.stopRequested && !this.samplingActive) {\n throw new Error(this.latestError ?? 'Allocation sampling did not restart.');\n }\n\n return { path: finalPath, sequence, timestamp, reason };\n }\n\n private async restartSamplingAfterFailure(): Promise<void> {\n try {\n await this.startSampling();\n } catch (error) {\n this.latestError = `Unable to restart allocation sampling: ${errorMessage(error)}`;\n this.state = 'error';\n }\n }\n\n private recordError(prefix: string, error: unknown): void {\n this.latestError = `${prefix}: ${errorMessage(error)}`;\n }\n\n private clearTimersAndObserver(): void {\n if (this.sampleTimer) clearInterval(this.sampleTimer);\n if (this.captureTimer) clearInterval(this.captureTimer);\n this.sampleTimer = null;\n this.captureTimer = null;\n this.observer?.disconnect();\n this.observer = null;\n }\n\n private disconnectInspector(): void {\n this.samplingActive = false;\n if (!this.inspector) return;\n try {\n this.inspector.disconnect();\n } catch {\n // Best-effort cleanup after an inspector failure.\n }\n this.inspector = null;\n }\n\n private async cleanupAfterStartFailure(): Promise<void> {\n this.clearTimersAndObserver();\n this.disconnectInspector();\n await this.writeQueue;\n try {\n if (this.outputDirectory) await rm(this.outputDirectory, { recursive: true, force: true });\n } catch (error) {\n this.latestError = `${this.latestError} Failed to remove partial artifacts: ${errorMessage(error)}`;\n } finally {\n this.outputDirectory = null;\n }\n }\n}\n"],"mappings":";;;;;;;;;AASA,MAAa,sCAAsC;CACjD,kBAAkB;CAClB,mBAAmB;CACnB,yBAAyB;AAC3B;AAEA,MAAa,sCAAsC;CACjD,kBAAkB;CAClB,mBAAmB;CACnB,yBAAyB;AAC3B;AAEA,MAAM,qBAAqB;AAC3B,MAAM,wBAAwB;AAsE9B,IAAa,sCAAb,cAAyD,MAAM;CAC7D,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;AAEA,SAAS,UAAU,OAAoC;CACrD,OAAO;EAAC;EAAK;EAAQ;EAAO;CAAI,CAAC,CAAC,SAAS,OAAO,KAAK,CAAC,CAAC,YAAY,KAAK,EAAE;AAC9E;AAEA,SAAS,oBACP,KACA,MACA,UACA,SACA,SACQ;CACR,MAAM,MAAM,IAAI;CAChB,IAAI,QAAQ,KAAA,KAAa,IAAI,KAAK,MAAM,IAAI,OAAO;CACnD,MAAM,QAAQ,OAAO,GAAG;CACxB,IAAI,CAAC,OAAO,cAAc,KAAK,KAAK,QAAQ,WAAY,YAAY,KAAA,KAAa,QAAQ,SAEvF,MAAM,IAAI,oCACR,GAAG,KAAK,sBAFI,YAAY,KAAA,IAAY,4BAA4B,YAAY,WAAW,QAAQ,OAAO,UAElE,aAAa,KAAK,UAAU,GAAG,EAAE,EACvE;CAEF,OAAO;AACT;AAEA,SAAS,mCAA2C;CAClD,IAAI,QAAQ,IAAI,qBAAqB,OAAO,KAAK,QAAQ,IAAI,qBAAqB,UAAU;CAQ5F,OAAO,KALLA,eAAa,WACT,KAAK,QAAQ,GAAG,WAAW,qBAAqB,IAChDA,eAAa,UACX,QAAQ,IAAI,WAAW,KAAK,QAAQ,GAAG,WAAW,SAAS,IAC3D,QAAQ,IAAI,iBAAiB,KAAK,QAAQ,GAAG,UAAU,OAAO,GAC3C,cAAc,UAAU;AACrD;AAEA,SAAgB,yCAAyC,MAA2C,QAAQ,KAG1G;CACA,OAAO;EACL,SAAS,UAAU,IAAI,kBAAkB;EACzC,QAAQ;GACN,iBAAiB,IAAI,wBAAwB,KAAK,KAAK,iCAAiC;GACxF,kBAAkB,oBAChB,KACA,yCACA,oCAAoC,kBACpC,oCAAoC,kBACpC,kBACF;GACA,mBAAmB,oBACjB,KACA,0CACA,oCAAoC,mBACpC,oCAAoC,mBACpC,kBACF;GACA,yBAAyB,oBACvB,KACA,gDACA,oCAAoC,yBACpC,oCAAoC,uBACtC;EACF;CACF;AACF;AAEA,SAAgB,8CACd,MAA2C,QAAQ,KACnD,eAAqD,CAAC,GACvB;CAC/B,IAAI;EACF,MAAM,EAAE,SAAS,WAAW,yCAAyC,GAAG;EACxE,OAAO;GAAE,aAAa,IAAI,yBAAyB,QAAQ,YAAY;GAAG;GAAS,OAAO;EAAK;CACjG,SAAS,OAAO;EACd,MAAM,SAAyC;GAC7C,iBAAiB,IAAI,wBAAwB,KAAK,KAAK,iCAAiC;GACxF,GAAG;EACL;EACA,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACrE,OAAO;GACL,aAAa,IAAI,yBAAyB,QAAQ,cAAc,OAAO;GACvE,SAAS,UAAU,IAAI,kBAAkB;GACzC,OAAO;EACT;CACF;AACF;AAEA,eAAsB,wCACpB,OACA,MACmC;CACnC,IAAI,CAAC,MAAM,SAAS,OAAO,MAAM;CACjC,IAAI,MAAM,OAAO;EACf,KAAK,gDAAgD,MAAM,OAAO;EAClE,OAAO,MAAM;CACf;CAEA,MAAM,SAAS,MAAM,MAAM,YAAY,MAAM;CAC7C,IAAI,OAAO,UAAU,UACnB,KAAK,gDAAgD,OAAO,SAAS,2BAA2B;CAElG,OAAO,MAAM;AACf;AAEA,eAAsB,wCACpB,aACA,MACA,YAAY,KACG;CACf,IAAI;CACJ,MAAM,WAAW,MAAM,QAAQ,KAAK,CAClC,YAAY,KAAK,CAAC,CAAC,WACX,QACN,UAAS;EACP,KAAK,oDAAoD,aAAa,KAAK,GAAG;EAC9E,OAAO;CACT,CACF,GACA,IAAI,SAAc,YAAW;EAC3B,UAAU,iBAAiB,QAAQ,IAAI,GAAG,SAAS;EACnD,QAAQ,MAAM;CAChB,CAAC,CACH,CAAC;CACD,IAAI,SAAS,aAAa,OAAO;CACjC,IAAI,UACF,KAAK,kDAAkD,UAAU,uCAAuC;AAC5G;AAEA,SAAS,0BAAmD;CAC1D,MAAM,UAAU,IAAI,QAAQ;CAC5B,OAAO;EACL,eAAe,QAAQ,QAAQ;EAC/B,kBAAkB,QAAQ,WAAW;EACrC,MAAM,OAAO,QAAQ,WAClB,MAAM,QAAQ,KACb,QACA,MACF;CACJ;AACF;AAEA,SAAS,2BAA2B,UAAiD;CAEnF,OAAO,IADc,qBAAoB,SAAQ,SAAS,KAAK,WAAW,CAAC,CAC7D;AAChB;AAEA,SAAS,aAAa,OAAwB;CAC5C,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEA,SAAS,cAAc,MAAoB;CACzC,OAAO,KAAK,YAAY,CAAC,CAAC,WAAW,KAAK,GAAG;AAC/C;AAEA,SAAS,WAAW,OAA6C;CAC/D,MAAM,QAAQ;AAChB;AAEA,IAAa,2BAAb,MAAsC;CACpC;CAEA,QAAyD;CACzD,kBAAyC;CACzC,YAAoD;CACpD,WAAsD;CACtD,cAA6D;CAC7D,eAA8D;CAC9D,YAAiC;CACjC,cAAsB;CACtB,eAAuB;CACvB,eAAuB;CACvB,eAAoE;CACpE,oBAA2C;CAC3C;CACA,iBAAyB;CACzB,gBAAwB;CACxB,kBAA0E;CAC1E,kBAA0E;CAC1E,0BAAkF;CAClF,mBAA2B;CAC3B,kBAA0D,CAAC;CAC3D,0BAAkC;CAClC,sBAA8B;CAC9B,eAAyC,QAAQ,QAAQ;CACzD,aAAuC,QAAQ,QAAQ;CAEvD;CACA;CACA;CAGA;CACA;CAEA,YACE,QACA,eAAqD,CAAC,GACtD,eAA8B,MAC9B;EACA,KAAK,SAAS,EAAE,GAAG,OAAO;EAC1B,KAAK,cAAc;EACnB,KAAK,cAAc;EACnB,KAAK,yBAAyB,aAAa,0BAA0B;EACrE,KAAK,4BAA4B,aAAa,6BAA6B;EAC3E,KAAK,MAAM,aAAa,8BAAc,IAAI,KAAK;EAC/C,KAAK,WAAW,aAAa,YAAY;CAC3C;CAEA,YAA4C;EAC1C,OAAO;GACL,OAAO,KAAK;GACZ,iBAAiB,KAAK;GACtB,QAAQ,EAAE,GAAG,KAAK,OAAO;GACzB,aAAa,KAAK;GAClB,cAAc,KAAK;GACnB,cAAc,KAAK;GACnB,cAAc,KAAK;GACnB,mBAAmB,KAAK;GACxB,OAAO,KAAK;EACd;CACF;CAEA,QAAiD;EAC/C,IAAI,KAAK,UAAU,UAAU,OAAO,QAAQ,QAAQ,KAAK,UAAU,CAAC;EACpE,IAAI,KAAK,UAAU,cAAc,KAAK,iBAAiB,OAAO,KAAK;EACnE,IAAI,KAAK,UAAU,cAAc,KAAK,iBAAiB;GACrD,KAAK,mBAAmB;GACxB,KAAK,4BAA4B,KAAK,gBAAgB,WAAW;IAC/D,KAAK,0BAA0B;IAC/B,IAAI,CAAC,KAAK,kBAAkB,OAAO,KAAK,UAAU;IAClD,KAAK,mBAAmB;IACxB,OAAO,KAAK,MAAM;GACpB,CAAC;GACD,OAAO,KAAK;EACd;EACA,IAAI,KAAK,aAAa;GACpB,KAAK,cAAc,KAAK;GACxB,KAAK,QAAQ;GACb,OAAO,QAAQ,QAAQ,KAAK,UAAU,CAAC;EACzC;EAEA,KAAK,QAAQ;EACb,KAAK,kBAAkB;EACvB,KAAK,mBAAmB;EACxB,KAAK,gBAAgB;EACrB,KAAK,kBAAkB;EACvB,KAAK,cAAc;EACnB,KAAK,eAAe;EACpB,KAAK,eAAe;EACpB,KAAK,kBAAkB,CAAC;EACxB,KAAK,0BAA0B;EAC/B,KAAK,sBAAsB;EAC3B,KAAK,eAAe;EACpB,KAAK,oBAAoB;EACzB,KAAK,cAAc;EACnB,KAAK,YAAY,KAAK,IAAI;EAE1B,MAAM,kBAAkB,KAAK,SAAS;EACtC,KAAK,kBAAkB;EACvB,gBAAqB,cAAc;GACjC,IAAI,KAAK,oBAAoB,iBAAiB,KAAK,kBAAkB;EACvE,CAAC;EACD,OAAO;CACT;CAEA,MAAc,WAAoD;EAChE,IAAI;GACF,MAAM,KAAK,gBAAgB;GAC3B,IAAI,MAAM,KAAK,sBAAsB,GAAG,OAAO,KAAK,UAAU;GAE9D,KAAK,UAAU;GACf,KAAK,YAAY,KAAK,uBAAuB;GAC7C,KAAK,UAAU,QAAQ;GACvB,MAAM,KAAK,cAAc;GACzB,IAAI,MAAM,KAAK,sBAAsB,GAAG,OAAO,KAAK,UAAU;GAE9D,MAAM,KAAK,WAAW;GACtB,IAAI,MAAM,KAAK,sBAAsB,GAAG,OAAO,KAAK,UAAU;GAE9D,KAAK,cAAc,kBAAkB;IACnC,KAAU,WAAW,CAAC,CAAC,OAAM,UAAS,KAAK,YAAY,yBAAyB,KAAK,CAAC;GACxF,GAAG,KAAK,OAAO,gBAAgB;GAC/B,WAAW,KAAK,WAAW;GAE3B,KAAK,eAAe,kBAAkB;IACpC,KAAU,QAAQ,UAAU,CAAC,CAAC,OAAM,UAAS,KAAK,YAAY,sCAAsC,KAAK,CAAC;GAC5G,GAAG,KAAK,OAAO,iBAAiB;GAChC,WAAW,KAAK,YAAY;GAE5B,KAAK,QAAQ;GACb,OAAO,KAAK,UAAU;EACxB,SAAS,OAAO;GACd,KAAK,cAAc,+CAA+C,aAAa,KAAK;GACpF,MAAM,KAAK,yBAAyB;GACpC,KAAK,QAAQ;GACb,OAAO,KAAK,UAAU;EACxB;CACF;CAEA,MAAc,wBAA0C;EACtD,IAAI,CAAC,KAAK,eAAe,OAAO;EAChC,MAAM,KAAK,yBAAyB;EACpC,KAAK,QAAQ;EACb,OAAO;CACT;CAEA,QAAQ,SAAgC,UAAoD;EAC1F,IAAI,KAAK,UAAU,UACjB,OAAO,QAAQ,OAAO,IAAI,MAAM,KAAK,eAAe,4CAA4C,CAAC;EAEnG,OAAO,KAAK,qBAAqB,KAAK,aAAa,QAAQ,KAAK,CAAC;CACnE;CAEA,MAAM,OAAgD;EACpD,IAAI,KAAK,iBAAiB;GACxB,KAAK,mBAAmB;GACxB,OAAO,KAAK;EACd;EACA,IAAI,KAAK,UAAU,YAAY,OAAO,KAAK,UAAU;EACrD,IAAI,KAAK,UAAU,WAAW,CAAC,KAAK,aAAa,CAAC,KAAK,iBAAiB,OAAO,KAAK,UAAU;EAE9F,MAAM,kBAAkB,KAAK,UAAU,aAAa,KAAK,kBAAkB;EAC3E,KAAK,gBAAgB;EACrB,KAAK,QAAQ;EACb,KAAK,uBAAuB;EAE5B,IAAI,iBAAiB;GACnB,KAAK,kBAAkB,gBAAgB,WAAW,KAAK,UAAU,CAAC;GAClE,OAAO,KAAK;EACd;EAEA,KAAK,mBAAmB,YAAY;GAClC,IAAI,YAA2B;GAC/B,IAAI;IACF,IAAI,KAAK,iBAAiB,MAAM,KAAK,WAAW;IAChD,MAAM,KAAK,eAAe,YAAY;KACpC,IAAI,KAAK,aAAa,KAAK,gBAAgB,MAAM,KAAK,aAAa,QAAQ,IAAI;IACjF,CAAC;IACD,MAAM,KAAK;IACX,IAAI,KAAK,yBACP,MAAM,IAAI,MAAM,4BAA4B,sBAAsB,yCAAyC;IAE7G,IAAI,KAAK,qBAAqB,MAAM,IAAI,MAAM,+DAA+D;IAC7G,KAAK,cAAc;GACrB,SAAS,OAAO;IACd,YAAY,sDAAsD,aAAa,KAAK;IACpF,KAAK,cAAc;GACrB,UAAU;IACR,KAAK,oBAAoB;IACzB,KAAK,QAAQ,YAAY,UAAU;GACrC;GACA,OAAO,KAAK,UAAU;EACxB,EAAA,CAAG;EAEH,OAAO,KAAK;CACd;CAEA,MAAc,kBAAiC;EAC7C,MAAM,MAAM,KAAK,OAAO,iBAAiB;GAAE,WAAW;GAAM,MAAM;EAAM,CAAC;EACzE,MAAM,MAAM,KAAK,OAAO,iBAAiB,GAAK;EAC9C,MAAM,UAAU,OAAO,cAAc,KAAK,SAAU,EAAE,GAAG,QAAQ,IAAI,GAAG,KAAK,SAAS,CAAC,CACpF,WAAW,kBAAkB,EAAE,CAAC,CAChC,MAAM,GAAG,EAAE;EACd,KAAK,kBAAkB,KAAK,KAAK,OAAO,iBAAiB,OAAO;EAChE,MAAM,MAAM,KAAK,iBAAiB,EAAE,MAAM,IAAM,CAAC;EACjD,MAAM,MAAM,KAAK,iBAAiB,GAAK;EAEvC,MAAM,WAAW;GACf,eAAe;GACf,OAAO;GACP,WAAW,KAAK,UAAW,YAAY;GACvC,KAAK,QAAQ;GACb,aAAa,QAAQ;GACrB,UAAA;GACA,MAAA;GACA,QAAQ,KAAK;EACf;EACA,MAAM,UAAU,KAAK,KAAK,iBAAiB,eAAe,GAAG,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,EAAE,KAAK;GACrG,MAAM;GACN,MAAM;EACR,CAAC;EACD,MAAM,UAAU,KAAK,KAAK,iBAAiB,uBAAuB,GAAG,IAAI;GAAE,MAAM;GAAM,MAAM;EAAM,CAAC;EACpG,MAAM,UAAU,KAAK,KAAK,iBAAiB,iBAAiB,GAAG,IAAI;GAAE,MAAM;GAAM,MAAM;EAAM,CAAC;CAChG;CAEA,YAA0B;EACxB,MAAM,WAAW,KAAK,2BAA0B,YAAW;GACzD,MAAM,SAAS,KAAK,cAAc,UAAU;GAC5C,MAAM,QAAQ,QAAQ,YAAY;GAClC,MAAM,YAAY,KAAK,IAAI,CAAC,CAAC,YAAY;GACzC,KAAK,MAAM,SAAS,SAAS;IAC3B,MAAM,UAAU;IAKhB,KAAK,gBAAgB;IACrB,IAAI,KAAK,gBAAgB,UAAU,uBAAuB;KACxD,KAAK,0BAA0B;KAC/B,KAAK,cAAc,4BAA4B,sBAAsB;KACrE;IACF;IACA,KAAK,gBAAgB,KAAK;KACxB;KACA,UAAU,KAAK;KACf,MAAM,MAAM;KACZ,WAAW,MAAM;KACjB,UAAU,MAAM;KAChB,MAAM,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ;KAC9C,OAAO,QAAQ,QAAQ,SAAS,QAAQ,SAAS;KACjD;KACA;KACA,sBAAsB,KAAK,cAAc,YAAY;IACvD,CAAC;GACH;EACF,CAAC;EACD,SAAS,QAAQ,EAAE,YAAY,CAAC,IAAI,EAAE,CAAC;EACvC,KAAK,WAAW;CAClB;CAEA,MAAc,gBAA+B;EAC3C,IAAI,CAAC,KAAK,WAAW,MAAM,IAAI,MAAM,mCAAmC;EACxE,MAAM,KAAK,UAAU,KAAK,8BAA8B;GACtD,kBAAkB,KAAK,OAAO;GAC9B,kCAAkC;GAClC,kCAAkC;EACpC,CAAC;EACD,KAAK,iBAAiB;CACxB;CAEA,MAAc,aAA4D;EACxE,IAAI,CAAC,KAAK,mBAAmB,CAAC,KAAK,WAAW,MAAM,IAAI,MAAM,8CAA8C;EAC5G,MAAM,SAA+C;GACnD,WAAW,KAAK,IAAI,CAAC,CAAC,YAAY;GAClC,UAAU,KAAK,cAAc;GAC7B,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,CAAC,CAAC,QAAQ,IAAI,KAAK,UAAU,QAAQ,CAAC;GACtE,QAAQ,QAAQ,YAAY;GAC5B,eAAe,QAAQ,cAAc;GACrC,MAAM,kBAAkB;GACxB,YAAY,uBAAuB;EACrC;EACA,KAAK,cAAc,OAAO;EAC1B,KAAK,eAAe;EACpB,MAAM,KAAK,aAAa,yBAAyB,MAAM;EACvD,MAAM,WAAW,KAAK,gBAAgB,OAAO,CAAC;EAC9C,IAAI,SAAS,SAAS,GAAG,MAAM,KAAK,kBAAkB,mBAAmB,QAAQ;EACjF,OAAO;CACT;CAEA,aAAqB,UAAkB,OAA+B;EACpE,OAAO,KAAK,kBAAkB,UAAU,CAAC,KAAK,CAAC;CACjD;CAEA,kBAA0B,UAAkB,QAAkC;EAC5E,MAAM,YAAY,KAAK,WAAW,KAAK,YAAY;GACjD,IAAI,CAAC,KAAK,mBAAmB,OAAO,WAAW,GAAG;GAClD,MAAM,QAAQ,OAAO,KAAI,UAAS,KAAK,UAAU,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;GAClE,IAAI;IACF,MAAM,WAAW,KAAK,KAAK,iBAAiB,QAAQ,GAAG,GAAG,MAAM,KAAK,EAAE,MAAM,IAAM,CAAC;GACtF,SAAS,OAAO;IACd,KAAK,sBAAsB;IAC3B,MAAM;GACR;EACF,CAAC;EACD,KAAK,aAAa,UAAU,YAAY,KAAA,CAAS;EACjD,OAAO;CACT;CAEA,eAA0B,WAAyC;EACjE,MAAM,SAAS,KAAK,aAAa,KAAK,SAAS;EAC/C,KAAK,eAAe,OAAO,YAAY,KAAA,CAAS;EAChD,OAAO;CACT;CAEA,MAAc,aACZ,QACA,OAC0C;EAC1C,IAAI,CAAC,KAAK,aAAa,CAAC,KAAK,kBAAkB,CAAC,KAAK,iBACnD,MAAM,IAAI,MAAM,KAAK,eAAe,oCAAoC;EAG1E,IAAI;EACJ,IAAI;GACF,WAAW,MAAM,KAAK,UAAU,KAAK,2BAA2B;GAChE,KAAK,iBAAiB;EACxB,SAAS,OAAO;GACd,KAAK,cAAc,8BAA8B,aAAa,KAAK;GACnE,IAAI,CAAC,SAAS,CAAC,KAAK,eAAe,MAAM,KAAK,4BAA4B;GAC1E,MAAM,IAAI,MAAM,KAAK,aAAa,EAAE,OAAO,MAAM,CAAC;EACpD;EAEA,MAAM,UAAU,SAAS;EACzB,IAAI,CAAC,WAAW,OAAO,YAAY,UAAU;GAC3C,KAAK,cAAc;GACnB,IAAI,CAAC,SAAS,CAAC,KAAK,eAAe,MAAM,KAAK,4BAA4B;GAC1E,MAAM,IAAI,MAAM,KAAK,WAAW;EAClC;EAEA,MAAM,WAAW,KAAK,eAAe;EACrC,MAAM,YAAY,KAAK,IAAI,CAAC,CAAC,YAAY;EACzC,MAAM,WAAW,cAAc,OAAO,QAAQ,CAAC,CAAC,SAAS,GAAG,GAAG,EAAE,GAAG,cAAc,IAAI,KAAK,SAAS,CAAC,EAAE;EACvG,MAAM,YAAY,KAAK,KAAK,iBAAiB,QAAQ;EACrD,MAAM,gBAAgB,KAAK,KAAK,iBAAiB,IAAI,SAAS,GAAG,KAAK,SAAS,EAAE,KAAK;EAEtF,IAAI;GACF,MAAM,UAAU,eAAe,GAAG,KAAK,UAAU,OAAO,EAAE,KAAK;IAAE,MAAM;IAAM,MAAM;GAAM,CAAC;GAC1F,MAAM,MAAM,eAAe,GAAK;GAChC,MAAM,OAAO,eAAe,SAAS;GACrC,MAAM,MAAM,WAAW,GAAK;GAC5B,KAAK,eAAe;GACpB,KAAK,oBAAoB;GACzB,KAAK,cAAc;EACrB,SAAS,OAAO;GACd,KAAK,sBAAsB;GAC3B,KAAK,cAAc,yCAAyC,aAAa,KAAK;GAC9E,MAAM,IAAI,MAAM,KAAK,aAAa,EAAE,OAAO,MAAM,CAAC;EACpD,UAAU;GACR,IAAI,CAAC,SAAS,CAAC,KAAK,iBAAiB,KAAK,UAAU,UAClD,IAAI;IACF,MAAM,KAAK,cAAc;GAC3B,SAAS,OAAO;IACd,KAAK,cAAc,0CAA0C,aAAa,KAAK;IAC/E,KAAK,QAAQ;GACf;EAEJ;EAEA,IAAI,CAAC,SAAS,CAAC,KAAK,iBAAiB,CAAC,KAAK,gBACzC,MAAM,IAAI,MAAM,KAAK,eAAe,sCAAsC;EAG5E,OAAO;GAAE,MAAM;GAAW;GAAU;GAAW;EAAO;CACxD;CAEA,MAAc,8BAA6C;EACzD,IAAI;GACF,MAAM,KAAK,cAAc;EAC3B,SAAS,OAAO;GACd,KAAK,cAAc,0CAA0C,aAAa,KAAK;GAC/E,KAAK,QAAQ;EACf;CACF;CAEA,YAAoB,QAAgB,OAAsB;EACxD,KAAK,cAAc,GAAG,OAAO,IAAI,aAAa,KAAK;CACrD;CAEA,yBAAuC;EACrC,IAAI,KAAK,aAAa,cAAc,KAAK,WAAW;EACpD,IAAI,KAAK,cAAc,cAAc,KAAK,YAAY;EACtD,KAAK,cAAc;EACnB,KAAK,eAAe;EACpB,KAAK,UAAU,WAAW;EAC1B,KAAK,WAAW;CAClB;CAEA,sBAAoC;EAClC,KAAK,iBAAiB;EACtB,IAAI,CAAC,KAAK,WAAW;EACrB,IAAI;GACF,KAAK,UAAU,WAAW;EAC5B,QAAQ,CAER;EACA,KAAK,YAAY;CACnB;CAEA,MAAc,2BAA0C;EACtD,KAAK,uBAAuB;EAC5B,KAAK,oBAAoB;EACzB,MAAM,KAAK;EACX,IAAI;GACF,IAAI,KAAK,iBAAiB,MAAM,GAAG,KAAK,iBAAiB;IAAE,WAAW;IAAM,OAAO;GAAK,CAAC;EAC3F,SAAS,OAAO;GACd,KAAK,cAAc,GAAG,KAAK,YAAY,uCAAuC,aAAa,KAAK;EAClG,UAAU;GACR,KAAK,kBAAkB;EACzB;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"process-memory-diagnostics.js","names":["platform"],"sources":["../src/process-memory-diagnostics.ts"],"sourcesContent":["import { appendFile, chmod, mkdir, rename, rm, writeFile } from 'node:fs/promises';\nimport { Session } from 'node:inspector/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { PerformanceObserver, type PerformanceEntry } from 'node:perf_hooks';\nimport { arch, platform } from 'node:process';\nimport { getHeapSpaceStatistics, getHeapStatistics } from 'node:v8';\n\nexport const PROCESS_MEMORY_DIAGNOSTICS_DEFAULTS = {\n sampleIntervalMs: 10_000,\n captureIntervalMs: 300_000,\n allocationIntervalBytes: 524_288,\n} as const;\n\nexport const PROCESS_MEMORY_DIAGNOSTICS_MINIMUMS = {\n sampleIntervalMs: 1_000,\n captureIntervalMs: 10_000,\n allocationIntervalBytes: 32_768,\n} as const;\n\nconst MAX_TIMER_DELAY_MS = 2_147_483_647;\nconst MAX_PENDING_GC_EVENTS = 1_000;\n\nexport interface ProcessMemoryDiagnosticsConfig {\n parentDirectory: string;\n sampleIntervalMs: number;\n captureIntervalMs: number;\n allocationIntervalBytes: number;\n}\n\nexport interface ProcessMemoryDiagnosticsEnvironment {\n MASTRACODE_PROFILE?: string;\n MASTRACODE_PROFILE_DIR?: string;\n MASTRACODE_PROFILE_SAMPLE_INTERVAL_MS?: string;\n MASTRACODE_PROFILE_CAPTURE_INTERVAL_MS?: string;\n MASTRACODE_PROFILE_ALLOCATION_INTERVAL_BYTES?: string;\n}\n\nexport interface ProcessMemoryDiagnosticsMemorySample {\n timestamp: string;\n sequence: number;\n elapsedMs: number;\n memory: ReturnType<typeof process.memoryUsage>;\n resourceUsage: ReturnType<typeof process.resourceUsage>;\n heap: ReturnType<typeof getHeapStatistics>;\n heapSpaces: ReturnType<typeof getHeapSpaceStatistics>;\n}\n\nexport interface ProcessMemoryDiagnosticsStatus {\n state: 'inactive' | 'starting' | 'active' | 'stopping' | 'error';\n outputDirectory: string | null;\n config: ProcessMemoryDiagnosticsConfig;\n sampleCount: number;\n captureCount: number;\n gcEventCount: number;\n latestSample: ProcessMemoryDiagnosticsMemorySample | null;\n latestCapturePath: string | null;\n error: string | null;\n}\n\nexport interface ProcessMemoryDiagnosticsCapture {\n path: string;\n sequence: number;\n timestamp: string;\n reason: 'manual' | 'periodic' | 'stop';\n}\n\ninterface InspectorSessionAdapter {\n connect(): void;\n disconnect(): void;\n post(method: string, params?: Record<string, unknown>): Promise<Record<string, unknown>>;\n}\n\ninterface PerformanceObserverAdapter {\n observe(options: Parameters<PerformanceObserver['observe']>[0]): void;\n disconnect(): void;\n}\n\ninterface ProcessMemoryDiagnosticsDependencies {\n createInspectorSession?: () => InspectorSessionAdapter;\n createPerformanceObserver?: (callback: (entries: PerformanceEntry[]) => void) => PerformanceObserverAdapter;\n now?: () => Date;\n randomId?: () => string;\n}\n\nexport interface ProcessMemoryDiagnosticsSetup {\n diagnostics: ProcessMemoryDiagnostics;\n enabled: boolean;\n error: string | null;\n}\n\nexport class ProcessMemoryDiagnosticsConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ProcessMemoryDiagnosticsConfigError';\n }\n}\n\nfunction isEnabled(value: string | undefined): boolean {\n return ['1', 'true', 'yes', 'on'].includes(value?.trim().toLowerCase() ?? '');\n}\n\nfunction parseBoundedInteger(\n env: ProcessMemoryDiagnosticsEnvironment,\n name: keyof ProcessMemoryDiagnosticsEnvironment,\n fallback: number,\n minimum: number,\n maximum?: number,\n): number {\n const raw = env[name];\n if (raw === undefined || raw.trim() === '') return fallback;\n const value = Number(raw);\n if (!Number.isSafeInteger(value) || value < minimum || (maximum !== undefined && value > maximum)) {\n const range = maximum === undefined ? `greater than or equal to ${minimum}` : `between ${minimum} and ${maximum}`;\n throw new ProcessMemoryDiagnosticsConfigError(\n `${name} must be an integer ${range}; received ${JSON.stringify(raw)}.`,\n );\n }\n return value;\n}\n\nfunction getDefaultProfileParentDirectory(): string {\n if (process.env.MASTRA_APP_DATA_DIR) return join(process.env.MASTRA_APP_DATA_DIR, 'profiles');\n\n const baseDirectory =\n platform === 'darwin'\n ? join(homedir(), 'Library', 'Application Support')\n : platform === 'win32'\n ? process.env.APPDATA || join(homedir(), 'AppData', 'Roaming')\n : process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share');\n return join(baseDirectory, 'mastracode', 'profiles');\n}\n\nexport function parseProcessMemoryDiagnosticsEnvironment(env: ProcessMemoryDiagnosticsEnvironment = process.env): {\n enabled: boolean;\n config: ProcessMemoryDiagnosticsConfig;\n} {\n return {\n enabled: isEnabled(env.MASTRACODE_PROFILE),\n config: {\n parentDirectory: env.MASTRACODE_PROFILE_DIR?.trim() || getDefaultProfileParentDirectory(),\n sampleIntervalMs: parseBoundedInteger(\n env,\n 'MASTRACODE_PROFILE_SAMPLE_INTERVAL_MS',\n PROCESS_MEMORY_DIAGNOSTICS_DEFAULTS.sampleIntervalMs,\n PROCESS_MEMORY_DIAGNOSTICS_MINIMUMS.sampleIntervalMs,\n MAX_TIMER_DELAY_MS,\n ),\n captureIntervalMs: parseBoundedInteger(\n env,\n 'MASTRACODE_PROFILE_CAPTURE_INTERVAL_MS',\n PROCESS_MEMORY_DIAGNOSTICS_DEFAULTS.captureIntervalMs,\n PROCESS_MEMORY_DIAGNOSTICS_MINIMUMS.captureIntervalMs,\n MAX_TIMER_DELAY_MS,\n ),\n allocationIntervalBytes: parseBoundedInteger(\n env,\n 'MASTRACODE_PROFILE_ALLOCATION_INTERVAL_BYTES',\n PROCESS_MEMORY_DIAGNOSTICS_DEFAULTS.allocationIntervalBytes,\n PROCESS_MEMORY_DIAGNOSTICS_MINIMUMS.allocationIntervalBytes,\n ),\n },\n };\n}\n\nexport function createProcessMemoryDiagnosticsFromEnvironment(\n env: ProcessMemoryDiagnosticsEnvironment = process.env,\n dependencies: ProcessMemoryDiagnosticsDependencies = {},\n): ProcessMemoryDiagnosticsSetup {\n try {\n const { enabled, config } = parseProcessMemoryDiagnosticsEnvironment(env);\n return { diagnostics: new ProcessMemoryDiagnostics(config, dependencies), enabled, error: null };\n } catch (error) {\n const config: ProcessMemoryDiagnosticsConfig = {\n parentDirectory: env.MASTRACODE_PROFILE_DIR?.trim() || getDefaultProfileParentDirectory(),\n ...PROCESS_MEMORY_DIAGNOSTICS_DEFAULTS,\n };\n const message = error instanceof Error ? error.message : String(error);\n return {\n diagnostics: new ProcessMemoryDiagnostics(config, dependencies, message),\n enabled: isEnabled(env.MASTRACODE_PROFILE),\n error: message,\n };\n }\n}\n\nexport async function startConfiguredProcessMemoryDiagnostics(\n setup: ProcessMemoryDiagnosticsSetup,\n warn: (message: string) => void,\n): Promise<ProcessMemoryDiagnostics> {\n if (!setup.enabled) return setup.diagnostics;\n if (setup.error) {\n warn(`Process memory diagnostics were not started: ${setup.error}`);\n return setup.diagnostics;\n }\n\n const status = await setup.diagnostics.start();\n if (status.state !== 'active') {\n warn(`Process memory diagnostics were not started: ${status.error ?? 'unknown inspector error'}`);\n }\n return setup.diagnostics;\n}\n\nexport async function stopProcessMemoryDiagnosticsWithTimeout(\n diagnostics: ProcessMemoryDiagnostics,\n warn: (message: string) => void,\n timeoutMs = 5_000,\n): Promise<void> {\n let timeout: ReturnType<typeof setTimeout> | undefined;\n const timedOut = await Promise.race([\n diagnostics.stop().then(\n () => false,\n error => {\n warn(`Process memory diagnostics did not stop cleanly: ${errorMessage(error)}`);\n return false;\n },\n ),\n new Promise<true>(resolve => {\n timeout = setTimeout(() => resolve(true), timeoutMs);\n timeout.unref();\n }),\n ]);\n if (timeout) clearTimeout(timeout);\n if (timedOut)\n warn(`Process memory diagnostics did not stop within ${timeoutMs}ms; final artifacts may be incomplete.`);\n}\n\nfunction defaultInspectorSession(): InspectorSessionAdapter {\n const session = new Session();\n return {\n connect: () => session.connect(),\n disconnect: () => session.disconnect(),\n post: async (method, params) =>\n (await session.post(\n method as Parameters<Session['post']>[0],\n params as Parameters<Session['post']>[1],\n )) as unknown as Record<string, unknown>,\n };\n}\n\nfunction defaultPerformanceObserver(callback: (entries: PerformanceEntry[]) => void) {\n const observer = new PerformanceObserver(list => callback(list.getEntries()));\n return observer;\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction safeTimestamp(date: Date): string {\n return date.toISOString().replaceAll(':', '-');\n}\n\nfunction unrefTimer(timer: ReturnType<typeof setInterval>): void {\n timer.unref?.();\n}\n\nexport class ProcessMemoryDiagnostics {\n readonly config: ProcessMemoryDiagnosticsConfig;\n\n private state: ProcessMemoryDiagnosticsStatus['state'] = 'inactive';\n private outputDirectory: string | null = null;\n private inspector: InspectorSessionAdapter | null = null;\n private observer: PerformanceObserverAdapter | null = null;\n private sampleTimer: ReturnType<typeof setInterval> | null = null;\n private captureTimer: ReturnType<typeof setInterval> | null = null;\n private startedAt: Date | null = null;\n private sampleCount = 0;\n private captureCount = 0;\n private gcEventCount = 0;\n private latestSample: ProcessMemoryDiagnosticsMemorySample | null = null;\n private latestCapturePath: string | null = null;\n private latestError: string | null;\n private samplingActive = false;\n private stopRequested = false;\n private startingPromise: Promise<ProcessMemoryDiagnosticsStatus> | null = null;\n private stoppingPromise: Promise<ProcessMemoryDiagnosticsStatus> | null = null;\n private restartAfterStopPromise: Promise<ProcessMemoryDiagnosticsStatus> | null = null;\n private restartRequested = false;\n private pendingGcEvents: Array<Record<string, unknown>> = [];\n private gcEventBufferOverflowed = false;\n private artifactWriteFailed = false;\n private captureQueue: Promise<unknown> = Promise.resolve();\n private writeQueue: Promise<unknown> = Promise.resolve();\n\n private readonly configError: string | null;\n private readonly createInspectorSession: () => InspectorSessionAdapter;\n private readonly createPerformanceObserver: NonNullable<\n ProcessMemoryDiagnosticsDependencies['createPerformanceObserver']\n >;\n private readonly now: () => Date;\n private readonly randomId: () => string;\n\n constructor(\n config: ProcessMemoryDiagnosticsConfig,\n dependencies: ProcessMemoryDiagnosticsDependencies = {},\n initialError: string | null = null,\n ) {\n this.config = { ...config };\n this.configError = initialError;\n this.latestError = initialError;\n this.createInspectorSession = dependencies.createInspectorSession ?? defaultInspectorSession;\n this.createPerformanceObserver = dependencies.createPerformanceObserver ?? defaultPerformanceObserver;\n this.now = dependencies.now ?? (() => new Date());\n this.randomId = dependencies.randomId ?? (() => globalThis.crypto.randomUUID());\n }\n\n getStatus(): ProcessMemoryDiagnosticsStatus {\n return {\n state: this.state,\n outputDirectory: this.outputDirectory,\n config: { ...this.config },\n sampleCount: this.sampleCount,\n captureCount: this.captureCount,\n gcEventCount: this.gcEventCount,\n latestSample: this.latestSample,\n latestCapturePath: this.latestCapturePath,\n error: this.latestError,\n };\n }\n\n start(): Promise<ProcessMemoryDiagnosticsStatus> {\n if (this.state === 'active') return Promise.resolve(this.getStatus());\n if (this.state === 'starting' && this.startingPromise) return this.startingPromise;\n if (this.state === 'stopping' && this.stoppingPromise) {\n this.restartRequested = true;\n this.restartAfterStopPromise ??= this.stoppingPromise.then(() => {\n this.restartAfterStopPromise = null;\n if (!this.restartRequested) return this.getStatus();\n this.restartRequested = false;\n return this.start();\n });\n return this.restartAfterStopPromise;\n }\n if (this.configError) {\n this.latestError = this.configError;\n this.state = 'error';\n return Promise.resolve(this.getStatus());\n }\n\n this.state = 'starting';\n this.stoppingPromise = null;\n this.restartRequested = false;\n this.stopRequested = false;\n this.outputDirectory = null;\n this.sampleCount = 0;\n this.captureCount = 0;\n this.gcEventCount = 0;\n this.pendingGcEvents = [];\n this.gcEventBufferOverflowed = false;\n this.artifactWriteFailed = false;\n this.latestSample = null;\n this.latestCapturePath = null;\n this.latestError = null;\n this.startedAt = this.now();\n\n const startingPromise = this.startRun();\n this.startingPromise = startingPromise;\n void startingPromise.finally(() => {\n if (this.startingPromise === startingPromise) this.startingPromise = null;\n });\n return startingPromise;\n }\n\n private async startRun(): Promise<ProcessMemoryDiagnosticsStatus> {\n try {\n await this.createArtifacts();\n if (await this.abortStartIfRequested()) return this.getStatus();\n\n this.observeGc();\n this.inspector = this.createInspectorSession();\n this.inspector.connect();\n await this.startSampling();\n if (await this.abortStartIfRequested()) return this.getStatus();\n\n await this.takeSample();\n if (await this.abortStartIfRequested()) return this.getStatus();\n\n this.sampleTimer = setInterval(() => {\n void this.takeSample().catch(error => this.recordError('Process sample failed', error));\n }, this.config.sampleIntervalMs);\n unrefTimer(this.sampleTimer);\n\n this.captureTimer = setInterval(() => {\n void this.capture('periodic').catch(error => this.recordError('Periodic allocation capture failed', error));\n }, this.config.captureIntervalMs);\n unrefTimer(this.captureTimer);\n\n this.state = 'active';\n return this.getStatus();\n } catch (error) {\n this.latestError = `Unable to start process memory diagnostics: ${errorMessage(error)}`;\n await this.cleanupAfterStartFailure();\n this.state = 'error';\n return this.getStatus();\n }\n }\n\n private async abortStartIfRequested(): Promise<boolean> {\n if (!this.stopRequested) return false;\n await this.cleanupAfterStartFailure();\n this.state = 'inactive';\n return true;\n }\n\n capture(reason: 'manual' | 'periodic' = 'manual'): Promise<ProcessMemoryDiagnosticsCapture> {\n if (this.state !== 'active') {\n return Promise.reject(new Error(this.latestError ?? 'Process memory diagnostics are not active.'));\n }\n return this.enqueueCapture(() => this.captureEpoch(reason, false));\n }\n\n async stop(): Promise<ProcessMemoryDiagnosticsStatus> {\n if (this.stoppingPromise) {\n this.restartRequested = false;\n return this.stoppingPromise;\n }\n if (this.state === 'inactive') return this.getStatus();\n if (this.state === 'error' && !this.inspector && !this.outputDirectory) return this.getStatus();\n\n const startingPromise = this.state === 'starting' ? this.startingPromise : null;\n this.stopRequested = true;\n this.state = 'stopping';\n this.clearTimersAndObserver();\n\n if (startingPromise) {\n this.stoppingPromise = startingPromise.then(() => this.getStatus());\n return this.stoppingPromise;\n }\n\n this.stoppingPromise = (async () => {\n let stopError: string | null = null;\n try {\n if (this.outputDirectory) await this.takeSample();\n await this.enqueueCapture(async () => {\n if (this.inspector && this.samplingActive) await this.captureEpoch('stop', true);\n });\n await this.writeQueue;\n if (this.gcEventBufferOverflowed) {\n throw new Error(`GC event buffer exceeded ${MAX_PENDING_GC_EVENTS} records before the next process sample.`);\n }\n if (this.artifactWriteFailed) throw new Error('One or more process memory diagnostic artifact writes failed.');\n this.latestError = null;\n } catch (error) {\n stopError = `Unable to stop process memory diagnostics cleanly: ${errorMessage(error)}`;\n this.latestError = stopError;\n } finally {\n this.disconnectInspector();\n this.state = stopError ? 'error' : 'inactive';\n }\n return this.getStatus();\n })();\n\n return this.stoppingPromise;\n }\n\n private async createArtifacts(): Promise<void> {\n await mkdir(this.config.parentDirectory, { recursive: true, mode: 0o700 });\n await chmod(this.config.parentDirectory, 0o700);\n const runName = `run-${safeTimestamp(this.startedAt!)}-${process.pid}-${this.randomId()\n .replaceAll(/[^a-zA-Z0-9-]/g, '')\n .slice(0, 12)}`;\n this.outputDirectory = join(this.config.parentDirectory, runName);\n await mkdir(this.outputDirectory, { mode: 0o700 });\n await chmod(this.outputDirectory, 0o700);\n\n const metadata = {\n schemaVersion: 1,\n runId: runName,\n startedAt: this.startedAt!.toISOString(),\n pid: process.pid,\n nodeVersion: process.version,\n platform,\n arch,\n config: this.config,\n };\n await writeFile(join(this.outputDirectory, 'metadata.json'), `${JSON.stringify(metadata, null, 2)}\\n`, {\n flag: 'wx',\n mode: 0o600,\n });\n await writeFile(join(this.outputDirectory, 'process-samples.jsonl'), '', { flag: 'wx', mode: 0o600 });\n await writeFile(join(this.outputDirectory, 'gc-events.jsonl'), '', { flag: 'wx', mode: 0o600 });\n }\n\n private observeGc(): void {\n const observer = this.createPerformanceObserver(entries => {\n const before = this.latestSample?.memory ?? null;\n const after = process.memoryUsage();\n const timestamp = this.now().toISOString();\n for (const entry of entries) {\n const gcEntry = entry as PerformanceEntry & {\n kind?: number;\n flags?: number;\n detail?: { kind?: number; flags?: number };\n };\n this.gcEventCount += 1;\n if (this.pendingGcEvents.length >= MAX_PENDING_GC_EVENTS) {\n this.gcEventBufferOverflowed = true;\n this.latestError = `GC event buffer exceeded ${MAX_PENDING_GC_EVENTS} records before the next process sample.`;\n continue;\n }\n this.pendingGcEvents.push({\n timestamp,\n sequence: this.gcEventCount,\n name: entry.name,\n startTime: entry.startTime,\n duration: entry.duration,\n kind: gcEntry.detail?.kind ?? gcEntry.kind ?? null,\n flags: gcEntry.detail?.flags ?? gcEntry.flags ?? null,\n before,\n after,\n latestSampleSequence: this.latestSample?.sequence ?? null,\n });\n }\n });\n observer.observe({ entryTypes: ['gc'] });\n this.observer = observer;\n }\n\n private async startSampling(): Promise<void> {\n if (!this.inspector) throw new Error('Inspector session is unavailable.');\n await this.inspector.post('HeapProfiler.startSampling', {\n samplingInterval: this.config.allocationIntervalBytes,\n includeObjectsCollectedByMajorGC: true,\n includeObjectsCollectedByMinorGC: true,\n });\n this.samplingActive = true;\n }\n\n private async takeSample(): Promise<ProcessMemoryDiagnosticsMemorySample> {\n if (!this.outputDirectory || !this.startedAt) throw new Error('Diagnostics output directory is unavailable.');\n const sample: ProcessMemoryDiagnosticsMemorySample = {\n timestamp: this.now().toISOString(),\n sequence: this.sampleCount + 1,\n elapsedMs: Math.max(0, this.now().getTime() - this.startedAt.getTime()),\n memory: process.memoryUsage(),\n resourceUsage: process.resourceUsage(),\n heap: getHeapStatistics(),\n heapSpaces: getHeapSpaceStatistics(),\n };\n this.sampleCount = sample.sequence;\n this.latestSample = sample;\n await this.enqueueWrite('process-samples.jsonl', sample);\n const gcEvents = this.pendingGcEvents.splice(0);\n if (gcEvents.length > 0) await this.enqueueWriteBatch('gc-events.jsonl', gcEvents);\n return sample;\n }\n\n private enqueueWrite(fileName: string, value: unknown): Promise<void> {\n return this.enqueueWriteBatch(fileName, [value]);\n }\n\n private enqueueWriteBatch(fileName: string, values: unknown[]): Promise<void> {\n const operation = this.writeQueue.then(async () => {\n if (!this.outputDirectory || values.length === 0) return;\n const lines = values.map(value => JSON.stringify(value)).join('\\n');\n try {\n await appendFile(join(this.outputDirectory, fileName), `${lines}\\n`, { mode: 0o600 });\n } catch (error) {\n this.artifactWriteFailed = true;\n throw error;\n }\n });\n this.writeQueue = operation.catch(() => undefined);\n return operation;\n }\n\n private enqueueCapture<T>(operation: () => Promise<T>): Promise<T> {\n const result = this.captureQueue.then(operation);\n this.captureQueue = result.catch(() => undefined);\n return result;\n }\n\n private async captureEpoch(\n reason: ProcessMemoryDiagnosticsCapture['reason'],\n final: boolean,\n ): Promise<ProcessMemoryDiagnosticsCapture> {\n if (!this.inspector || !this.samplingActive || !this.outputDirectory) {\n throw new Error(this.latestError ?? 'Allocation sampling is not active.');\n }\n\n let response: Record<string, unknown>;\n try {\n response = await this.inspector.post('HeapProfiler.stopSampling');\n this.samplingActive = false;\n } catch (error) {\n this.latestError = `Allocation capture failed: ${errorMessage(error)}`;\n if (!final && !this.stopRequested) await this.restartSamplingAfterFailure();\n throw new Error(this.latestError, { cause: error });\n }\n\n const profile = response.profile;\n if (!profile || typeof profile !== 'object') {\n this.latestError = 'Allocation capture failed: inspector returned no profile.';\n if (!final && !this.stopRequested) await this.restartSamplingAfterFailure();\n throw new Error(this.latestError);\n }\n\n const sequence = this.captureCount + 1;\n const timestamp = this.now().toISOString();\n const filename = `allocation-${String(sequence).padStart(6, '0')}-${safeTimestamp(new Date(timestamp))}.heapprofile`;\n const finalPath = join(this.outputDirectory, filename);\n const temporaryPath = join(this.outputDirectory, `.${filename}.${this.randomId()}.tmp`);\n\n try {\n await writeFile(temporaryPath, `${JSON.stringify(profile)}\\n`, { flag: 'wx', mode: 0o600 });\n await chmod(temporaryPath, 0o600);\n await rename(temporaryPath, finalPath);\n await chmod(finalPath, 0o600);\n this.captureCount = sequence;\n this.latestCapturePath = finalPath;\n this.latestError = null;\n } catch (error) {\n this.artifactWriteFailed = true;\n this.latestError = `Unable to persist allocation capture: ${errorMessage(error)}`;\n throw new Error(this.latestError, { cause: error });\n } finally {\n if (!final && !this.stopRequested && this.state === 'active') {\n try {\n await this.startSampling();\n } catch (error) {\n this.latestError = `Unable to restart allocation sampling: ${errorMessage(error)}`;\n this.state = 'error';\n }\n }\n }\n\n if (!final && !this.stopRequested && !this.samplingActive) {\n throw new Error(this.latestError ?? 'Allocation sampling did not restart.');\n }\n\n return { path: finalPath, sequence, timestamp, reason };\n }\n\n private async restartSamplingAfterFailure(): Promise<void> {\n try {\n await this.startSampling();\n } catch (error) {\n this.latestError = `Unable to restart allocation sampling: ${errorMessage(error)}`;\n this.state = 'error';\n }\n }\n\n private recordError(prefix: string, error: unknown): void {\n this.latestError = `${prefix}: ${errorMessage(error)}`;\n }\n\n private clearTimersAndObserver(): void {\n if (this.sampleTimer) clearInterval(this.sampleTimer);\n if (this.captureTimer) clearInterval(this.captureTimer);\n this.sampleTimer = null;\n this.captureTimer = null;\n this.observer?.disconnect();\n this.observer = null;\n }\n\n private disconnectInspector(): void {\n this.samplingActive = false;\n if (!this.inspector) return;\n try {\n this.inspector.disconnect();\n } catch {\n // Best-effort cleanup after an inspector failure.\n }\n this.inspector = null;\n }\n\n private async cleanupAfterStartFailure(): Promise<void> {\n this.clearTimersAndObserver();\n this.disconnectInspector();\n await this.writeQueue;\n try {\n if (this.outputDirectory) await rm(this.outputDirectory, { recursive: true, force: true });\n } catch (error) {\n this.latestError = `${this.latestError} Failed to remove partial artifacts: ${errorMessage(error)}`;\n } finally {\n this.outputDirectory = null;\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { parseInterval, validateInterval, nextFireTime } from './interval.js';
|
|
2
|
+
export type { ParsedInterval, IntervalError, IntervalCheck } from './interval.js';
|
|
3
|
+
export { assembleSchedulePrompt, formatScriptOutput, SCRIPT_TIMEOUT_MS } from './prompt.js';
|
|
4
|
+
export type { RunScript, ScriptResult, AssemblePromptOptions, AssembledPrompt } from './prompt.js';
|
|
5
|
+
export { ThreadScheduler, shortScheduleId, describeScheduleSource, scheduleSignalAttributes, SCHEDULE_SIGNAL_SOURCE, LATE_FIRE_GRACE_MS, } from './scheduler.js';
|
|
6
|
+
export type { ThreadSchedule, ThreadScheduleSpec, ThreadScheduleSource, ScheduleTrigger, ScheduleFile, ThreadSchedulerOptions, ScheduleRunResult, } from './scheduler.js';
|
|
7
|
+
export { runScript, resolveScriptCommand, isExecutableFile } from './run-script.js';
|
|
8
|
+
export { createScheduleTools, SCHEDULE_TOOL_IDS } from './tools.js';
|
|
9
|
+
export type { ScheduleToolsOptions, ScheduleFileOptions } from './tools.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schedules/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC9E,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC5F,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnG,OAAO,EACL,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpE,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { nextFireTime, parseInterval, validateInterval } from "./interval.js";
|
|
2
|
+
import { SCRIPT_TIMEOUT_MS, assembleSchedulePrompt, formatScriptOutput } from "./prompt.js";
|
|
3
|
+
import { LATE_FIRE_GRACE_MS, SCHEDULE_SIGNAL_SOURCE, ThreadScheduler, describeScheduleSource, scheduleSignalAttributes, shortScheduleId } from "./scheduler.js";
|
|
4
|
+
import { isExecutableFile, resolveScriptCommand, runScript } from "./run-script.js";
|
|
5
|
+
import { SCHEDULE_TOOL_IDS, createScheduleTools } from "./tools.js";
|
|
6
|
+
export { LATE_FIRE_GRACE_MS, SCHEDULE_SIGNAL_SOURCE, SCHEDULE_TOOL_IDS, SCRIPT_TIMEOUT_MS, ThreadScheduler, assembleSchedulePrompt, createScheduleTools, describeScheduleSource, formatScriptOutput, isExecutableFile, nextFireTime, parseInterval, resolveScriptCommand, runScript, scheduleSignalAttributes, shortScheduleId, validateInterval };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interval parsing and fire-time math for `/schedules`.
|
|
3
|
+
*
|
|
4
|
+
* Users type `5m`, `2h`, `1 day`. Schedules fire on local wall-clock
|
|
5
|
+
* boundaries (5m fires at :00, :05, ...), so only intervals that divide the
|
|
6
|
+
* hour (minutes) or the day (hours) give a stable cadence; anything else is
|
|
7
|
+
* rejected with the nearest valid suggestion instead of silently drifting.
|
|
8
|
+
*/
|
|
9
|
+
export type ParsedInterval = {
|
|
10
|
+
ms: number;
|
|
11
|
+
label: string;
|
|
12
|
+
};
|
|
13
|
+
export type IntervalError = {
|
|
14
|
+
error: string;
|
|
15
|
+
};
|
|
16
|
+
/** Parse `5m`, `2h`, `1 day`, `90 minutes` into milliseconds plus a normalized label. */
|
|
17
|
+
export declare function parseInterval(text: string): ParsedInterval | IntervalError;
|
|
18
|
+
export type IntervalCheck = {
|
|
19
|
+
ok: true;
|
|
20
|
+
} | {
|
|
21
|
+
error: string;
|
|
22
|
+
suggestion?: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Check that an interval fires on stable clock boundaries: whole minutes that
|
|
26
|
+
* divide the hour, whole hours that divide the day, or exactly one day.
|
|
27
|
+
*/
|
|
28
|
+
export declare function validateInterval({ ms }: {
|
|
29
|
+
ms: number;
|
|
30
|
+
}): IntervalCheck;
|
|
31
|
+
/**
|
|
32
|
+
* The first boundary strictly after `after`, on the local wall clock
|
|
33
|
+
* (5m → :00, :05, ...; 2h → 00:00, 02:00, ...; 1d → midnight). `intervalMs`
|
|
34
|
+
* must pass {@link validateInterval}.
|
|
35
|
+
*
|
|
36
|
+
* Around daylight-saving changes this behaves like cron:
|
|
37
|
+
* - Sub-hour cadences follow the minute hand, so they keep firing every N real
|
|
38
|
+
* minutes through both the skipped and the repeated hour. Across a
|
|
39
|
+
* half-hour change, the gap between fires never exceeds the interval.
|
|
40
|
+
* - Hourly and daily cadences fire once per wall-clock slot. A boundary that
|
|
41
|
+
* falls in a skipped hour fires at the first minute after the jump (02:00 →
|
|
42
|
+
* 03:00), and a repeated hour does not fire a second time.
|
|
43
|
+
*
|
|
44
|
+
* Computing from `after` rather than from the previous fire means a late
|
|
45
|
+
* timer (sleep, busy event loop) skips missed boundaries instead of bursting
|
|
46
|
+
* to catch up.
|
|
47
|
+
*/
|
|
48
|
+
export declare function nextFireTime(intervalMs: number, after: number): number;
|
|
49
|
+
//# sourceMappingURL=interval.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interval.d.ts","sourceRoot":"","sources":["../../src/schedules/interval.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,MAAM,MAAM,cAAc,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAC3D,MAAM,MAAM,aAAa,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAyB9C,yFAAyF;AACzF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,aAAa,CAc1E;AAED,MAAM,MAAM,aAAa,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAWlF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,EAAE,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,aAAa,CA6CtE;AAUD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAkBtE"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
//#region src/schedules/interval.ts
|
|
2
|
+
/**
|
|
3
|
+
* Interval parsing and fire-time math for `/schedules`.
|
|
4
|
+
*
|
|
5
|
+
* Users type `5m`, `2h`, `1 day`. Schedules fire on local wall-clock
|
|
6
|
+
* boundaries (5m fires at :00, :05, ...), so only intervals that divide the
|
|
7
|
+
* hour (minutes) or the day (hours) give a stable cadence; anything else is
|
|
8
|
+
* rejected with the nearest valid suggestion instead of silently drifting.
|
|
9
|
+
*/
|
|
10
|
+
const SECOND_MS = 1e3;
|
|
11
|
+
const MINUTE_MS = 60 * SECOND_MS;
|
|
12
|
+
const HOUR_MS = 60 * MINUTE_MS;
|
|
13
|
+
const DAY_MS = 24 * HOUR_MS;
|
|
14
|
+
const UNIT_MS = {
|
|
15
|
+
s: {
|
|
16
|
+
ms: SECOND_MS,
|
|
17
|
+
short: "s"
|
|
18
|
+
},
|
|
19
|
+
sec: {
|
|
20
|
+
ms: SECOND_MS,
|
|
21
|
+
short: "s"
|
|
22
|
+
},
|
|
23
|
+
secs: {
|
|
24
|
+
ms: SECOND_MS,
|
|
25
|
+
short: "s"
|
|
26
|
+
},
|
|
27
|
+
second: {
|
|
28
|
+
ms: SECOND_MS,
|
|
29
|
+
short: "s"
|
|
30
|
+
},
|
|
31
|
+
seconds: {
|
|
32
|
+
ms: SECOND_MS,
|
|
33
|
+
short: "s"
|
|
34
|
+
},
|
|
35
|
+
m: {
|
|
36
|
+
ms: MINUTE_MS,
|
|
37
|
+
short: "m"
|
|
38
|
+
},
|
|
39
|
+
min: {
|
|
40
|
+
ms: MINUTE_MS,
|
|
41
|
+
short: "m"
|
|
42
|
+
},
|
|
43
|
+
mins: {
|
|
44
|
+
ms: MINUTE_MS,
|
|
45
|
+
short: "m"
|
|
46
|
+
},
|
|
47
|
+
minute: {
|
|
48
|
+
ms: MINUTE_MS,
|
|
49
|
+
short: "m"
|
|
50
|
+
},
|
|
51
|
+
minutes: {
|
|
52
|
+
ms: MINUTE_MS,
|
|
53
|
+
short: "m"
|
|
54
|
+
},
|
|
55
|
+
h: {
|
|
56
|
+
ms: HOUR_MS,
|
|
57
|
+
short: "h"
|
|
58
|
+
},
|
|
59
|
+
hr: {
|
|
60
|
+
ms: HOUR_MS,
|
|
61
|
+
short: "h"
|
|
62
|
+
},
|
|
63
|
+
hrs: {
|
|
64
|
+
ms: HOUR_MS,
|
|
65
|
+
short: "h"
|
|
66
|
+
},
|
|
67
|
+
hour: {
|
|
68
|
+
ms: HOUR_MS,
|
|
69
|
+
short: "h"
|
|
70
|
+
},
|
|
71
|
+
hours: {
|
|
72
|
+
ms: HOUR_MS,
|
|
73
|
+
short: "h"
|
|
74
|
+
},
|
|
75
|
+
d: {
|
|
76
|
+
ms: DAY_MS,
|
|
77
|
+
short: "d"
|
|
78
|
+
},
|
|
79
|
+
day: {
|
|
80
|
+
ms: DAY_MS,
|
|
81
|
+
short: "d"
|
|
82
|
+
},
|
|
83
|
+
days: {
|
|
84
|
+
ms: DAY_MS,
|
|
85
|
+
short: "d"
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const INTERVAL_RE = /^(\d+)\s*([a-z]+)$/i;
|
|
89
|
+
/** Parse `5m`, `2h`, `1 day`, `90 minutes` into milliseconds plus a normalized label. */
|
|
90
|
+
function parseInterval(text) {
|
|
91
|
+
const match = INTERVAL_RE.exec(text.trim());
|
|
92
|
+
if (!match) return { error: `Invalid interval "${text}". Use a number followed by a unit, e.g. 5m, 2h, 1d.` };
|
|
93
|
+
const value = Number(match[1]);
|
|
94
|
+
const unit = UNIT_MS[match[2].toLowerCase()];
|
|
95
|
+
if (!unit) return { error: `Unknown interval unit "${match[2]}". Use s, m, h, or d.` };
|
|
96
|
+
if (value <= 0) return { error: "Interval must be greater than zero." };
|
|
97
|
+
return {
|
|
98
|
+
ms: value * unit.ms,
|
|
99
|
+
label: `${value}${unit.short}`
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const MINUTE_DIVISORS = [
|
|
103
|
+
1,
|
|
104
|
+
2,
|
|
105
|
+
3,
|
|
106
|
+
4,
|
|
107
|
+
5,
|
|
108
|
+
6,
|
|
109
|
+
10,
|
|
110
|
+
12,
|
|
111
|
+
15,
|
|
112
|
+
20,
|
|
113
|
+
30,
|
|
114
|
+
60
|
|
115
|
+
];
|
|
116
|
+
const HOUR_DIVISORS = [
|
|
117
|
+
1,
|
|
118
|
+
2,
|
|
119
|
+
3,
|
|
120
|
+
4,
|
|
121
|
+
6,
|
|
122
|
+
8,
|
|
123
|
+
12,
|
|
124
|
+
24
|
|
125
|
+
];
|
|
126
|
+
function nearest(candidates, target) {
|
|
127
|
+
return candidates.reduce((best, candidate) => Math.abs(candidate - target) < Math.abs(best - target) ? candidate : best);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Check that an interval fires on stable clock boundaries: whole minutes that
|
|
131
|
+
* divide the hour, whole hours that divide the day, or exactly one day.
|
|
132
|
+
*/
|
|
133
|
+
function validateInterval({ ms }) {
|
|
134
|
+
if (ms < MINUTE_MS) return {
|
|
135
|
+
error: "Schedules fire at most once per minute.",
|
|
136
|
+
suggestion: "1m"
|
|
137
|
+
};
|
|
138
|
+
if (ms % MINUTE_MS !== 0) {
|
|
139
|
+
const minutes = Math.round(ms / MINUTE_MS);
|
|
140
|
+
return {
|
|
141
|
+
error: "Interval must be a whole number of minutes.",
|
|
142
|
+
suggestion: `${Math.max(1, nearest(MINUTE_DIVISORS, minutes))}m`
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const minutes = ms / MINUTE_MS;
|
|
146
|
+
if (minutes < 60) {
|
|
147
|
+
if (60 % minutes === 0) return { ok: true };
|
|
148
|
+
return {
|
|
149
|
+
error: `${minutes}m does not divide an hour evenly.`,
|
|
150
|
+
suggestion: `${nearest(MINUTE_DIVISORS, minutes)}m`
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (minutes % 60 !== 0) {
|
|
154
|
+
const hours = Math.round(minutes / 60);
|
|
155
|
+
return {
|
|
156
|
+
error: `${minutes}m is not a whole number of hours.`,
|
|
157
|
+
suggestion: hours < 24 ? `${nearest(HOUR_DIVISORS, hours)}h` : "1d"
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const hours = minutes / 60;
|
|
161
|
+
if (hours < 24) {
|
|
162
|
+
if (24 % hours === 0) return { ok: true };
|
|
163
|
+
return {
|
|
164
|
+
error: `${hours}h does not divide a day evenly.`,
|
|
165
|
+
suggestion: `${nearest(HOUR_DIVISORS, hours)}h`
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (hours === 24) return { ok: true };
|
|
169
|
+
return {
|
|
170
|
+
error: "Intervals longer than one day are not supported.",
|
|
171
|
+
suggestion: "1d"
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/** Hour-and-longer cadences: which local wall-clock slot a minute belongs to. */
|
|
175
|
+
function hourSlotKey(date, stepHours) {
|
|
176
|
+
return ((date.getFullYear() * 12 + date.getMonth()) * 31 + date.getDate()) * 24 + Math.floor(date.getHours() / stepHours) * stepHours;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* The first boundary strictly after `after`, on the local wall clock
|
|
180
|
+
* (5m → :00, :05, ...; 2h → 00:00, 02:00, ...; 1d → midnight). `intervalMs`
|
|
181
|
+
* must pass {@link validateInterval}.
|
|
182
|
+
*
|
|
183
|
+
* Around daylight-saving changes this behaves like cron:
|
|
184
|
+
* - Sub-hour cadences follow the minute hand, so they keep firing every N real
|
|
185
|
+
* minutes through both the skipped and the repeated hour. Across a
|
|
186
|
+
* half-hour change, the gap between fires never exceeds the interval.
|
|
187
|
+
* - Hourly and daily cadences fire once per wall-clock slot. A boundary that
|
|
188
|
+
* falls in a skipped hour fires at the first minute after the jump (02:00 →
|
|
189
|
+
* 03:00), and a repeated hour does not fire a second time.
|
|
190
|
+
*
|
|
191
|
+
* Computing from `after` rather than from the previous fire means a late
|
|
192
|
+
* timer (sleep, busy event loop) skips missed boundaries instead of bursting
|
|
193
|
+
* to catch up.
|
|
194
|
+
*/
|
|
195
|
+
function nextFireTime(intervalMs, after) {
|
|
196
|
+
const cursor = /* @__PURE__ */ new Date(Math.floor(after / MINUTE_MS) * MINUTE_MS);
|
|
197
|
+
const stepMinutes = intervalMs / MINUTE_MS;
|
|
198
|
+
if (stepMinutes < 60) {
|
|
199
|
+
const latest = cursor.getTime() + intervalMs;
|
|
200
|
+
do
|
|
201
|
+
cursor.setTime(cursor.getTime() + MINUTE_MS);
|
|
202
|
+
while (cursor.getMinutes() % stepMinutes !== 0 && cursor.getTime() < latest);
|
|
203
|
+
return cursor.getTime();
|
|
204
|
+
}
|
|
205
|
+
const stepHours = stepMinutes / 60;
|
|
206
|
+
const startSlot = hourSlotKey(new Date(after), stepHours);
|
|
207
|
+
do
|
|
208
|
+
cursor.setTime(cursor.getTime() + MINUTE_MS);
|
|
209
|
+
while (hourSlotKey(cursor, stepHours) <= startSlot);
|
|
210
|
+
return cursor.getTime();
|
|
211
|
+
}
|
|
212
|
+
//#endregion
|
|
213
|
+
export { nextFireTime, parseInterval, validateInterval };
|
|
214
|
+
|
|
215
|
+
//# sourceMappingURL=interval.js.map
|