@geminixiang/mikan 1.0.0-beta.11 → 1.0.0-beta.2
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/CHANGELOG.md +0 -142
- package/dist/adapter.d.ts +1 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/discord/bot.d.ts +0 -1
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +0 -9
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +0 -1
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +1 -18
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
- package/dist/adapters/slack/response-lifecycle.js +0 -6
- package/dist/adapters/slack/response-lifecycle.js.map +1 -1
- package/dist/adapters/telegram/bot.d.ts +0 -1
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +0 -10
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/agent.d.ts +2 -2
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +11 -34
- package/dist/agent.js.map +1 -1
- package/dist/commands/extensions.d.ts.map +1 -1
- package/dist/commands/extensions.js +5 -14
- package/dist/commands/extensions.js.map +1 -1
- package/dist/execution-resolver.d.ts.map +1 -1
- package/dist/execution-resolver.js +2 -11
- package/dist/execution-resolver.js.map +1 -1
- package/dist/harness/extensions/loader.d.ts +9 -33
- package/dist/harness/extensions/loader.d.ts.map +1 -1
- package/dist/harness/extensions/loader.js +50 -226
- package/dist/harness/extensions/loader.js.map +1 -1
- package/dist/harness/extensions/types.d.ts +4 -27
- package/dist/harness/extensions/types.d.ts.map +1 -1
- package/dist/harness/extensions/types.js.map +1 -1
- package/dist/harness/index.d.ts +1 -1
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +1 -1
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +1 -5
- package/dist/harness/runner.js.map +1 -1
- package/dist/main.js +8 -83
- package/dist/main.js.map +1 -1
- package/dist/runtime/conversation-runtime.js +1 -1
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +1 -5
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/sessions/policy.d.ts.map +1 -1
- package/dist/sessions/policy.js +0 -1
- package/dist/sessions/policy.js.map +1 -1
- package/dist/tools/index.d.ts +1 -4
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +0 -12
- package/dist/tools/index.js.map +1 -1
- package/dist/types.d.ts +1 -53
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -2
- package/dist/adapters/github/bot.d.ts +0 -71
- package/dist/adapters/github/bot.d.ts.map +0 -1
- package/dist/adapters/github/bot.js +0 -626
- package/dist/adapters/github/bot.js.map +0 -1
- package/dist/adapters/github/client.d.ts +0 -81
- package/dist/adapters/github/client.d.ts.map +0 -1
- package/dist/adapters/github/client.js +0 -208
- package/dist/adapters/github/client.js.map +0 -1
- package/dist/adapters/github/context.d.ts +0 -9
- package/dist/adapters/github/context.d.ts.map +0 -1
- package/dist/adapters/github/context.js +0 -170
- package/dist/adapters/github/context.js.map +0 -1
- package/dist/adapters/github/ids.d.ts +0 -25
- package/dist/adapters/github/ids.d.ts.map +0 -1
- package/dist/adapters/github/ids.js +0 -39
- package/dist/adapters/github/ids.js.map +0 -1
- package/dist/adapters/github/repo.d.ts +0 -19
- package/dist/adapters/github/repo.d.ts.map +0 -1
- package/dist/adapters/github/repo.js +0 -58
- package/dist/adapters/github/repo.js.map +0 -1
- package/dist/adapters/github/types.d.ts +0 -123
- package/dist/adapters/github/types.d.ts.map +0 -1
- package/dist/adapters/github/types.js +0 -2
- package/dist/adapters/github/types.js.map +0 -1
- package/dist/cli/ext-git.d.ts +0 -22
- package/dist/cli/ext-git.d.ts.map +0 -1
- package/dist/cli/ext-git.js +0 -76
- package/dist/cli/ext-git.js.map +0 -1
- package/dist/cli/ext.d.ts +0 -2
- package/dist/cli/ext.d.ts.map +0 -1
- package/dist/cli/ext.js +0 -217
- package/dist/cli/ext.js.map +0 -1
- package/dist/tools/github-checks.d.ts +0 -17
- package/dist/tools/github-checks.d.ts.map +0 -1
- package/dist/tools/github-checks.js +0 -68
- package/dist/tools/github-checks.js.map +0 -1
- package/dist/tools/github-pr.d.ts +0 -22
- package/dist/tools/github-pr.d.ts.map +0 -1
- package/dist/tools/github-pr.js +0 -55
- package/dist/tools/github-pr.js.map +0 -1
- package/dist/tools/react.d.ts +0 -15
- package/dist/tools/react.d.ts.map +0 -1
- package/dist/tools/react.js +0 -40
- package/dist/tools/react.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execution-resolver.d.ts","sourceRoot":"","sources":["../src/execution-resolver.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,sBAAsB,EAAuB,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EAAkB,KAAK,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEvF,OAAO,EAAgD,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAInG,YAAY,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAExE,wBAAgB,kCAAkC,CAChD,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,cAAc,EAAE,MAAM,GACrB,uBAAuB,CAiBzB;AAiCD,qBAAa,sBAAsB;IAI/B,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,WAAW,CAAC;IACpB,OAAO,CAAC,YAAY,CAAC;IANvB,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAqB;IAE7D,YACU,UAAU,EAAE,aAAa,EACzB,YAAY,EAAE,YAAY,EAC1B,WAAW,CAAC,EAAE,sBAAsB,YAAA,EACpC,YAAY,CAAC,EAAE,MAAM,YAAA,EAC3B;IAEE,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAatD;IAED,OAAO,CAAC,wBAAwB;
|
|
1
|
+
{"version":3,"file":"execution-resolver.d.ts","sourceRoot":"","sources":["../src/execution-resolver.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,sBAAsB,EAAuB,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EAAkB,KAAK,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEvF,OAAO,EAAgD,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAInG,YAAY,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAExE,wBAAgB,kCAAkC,CAChD,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,cAAc,EAAE,MAAM,GACrB,uBAAuB,CAiBzB;AAiCD,qBAAa,sBAAsB;IAI/B,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,WAAW,CAAC;IACpB,OAAO,CAAC,YAAY,CAAC;IANvB,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAqB;IAE7D,YACU,UAAU,EAAE,aAAa,EACzB,YAAY,EAAE,YAAY,EAC1B,WAAW,CAAC,EAAE,sBAAsB,YAAA,EACpC,YAAY,CAAC,EAAE,MAAM,YAAA,EAC3B;IAEE,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAatD;IAED,OAAO,CAAC,wBAAwB;IAehC,OAAO,CAAC,oBAAoB;IAgB5B,OAAO,CAAC,wBAAwB;IA4ChC,OAAO,CAAC,aAAa;IA0BrB,OAAO,CAAC,uBAAuB;CAsBhC"}
|
|
@@ -51,22 +51,13 @@ export class ActorExecutionResolver {
|
|
|
51
51
|
}
|
|
52
52
|
async resolve(context) {
|
|
53
53
|
const vaultKey = resolveActorVaultKey(this.baseConfig, context.userId, context.conversationId);
|
|
54
|
-
this.ensureDefaultSharedVault(vaultKey
|
|
54
|
+
this.ensureDefaultSharedVault(vaultKey);
|
|
55
55
|
const vault = this.vaultManager.resolve(vaultKey);
|
|
56
56
|
const config = this.resolveSandboxConfig(vaultKey);
|
|
57
57
|
const env = config.type !== "host" && vault && Object.keys(vault.env).length > 0 ? vault.env : undefined;
|
|
58
58
|
return createExecutor(config, env, this.buildEnsureReadyCallback(vaultKey, context.conversationId, config, vault));
|
|
59
59
|
}
|
|
60
|
-
ensureDefaultSharedVault(vaultKey
|
|
61
|
-
// The ambient default vault is a membership-trust convenience: on Slack/
|
|
62
|
-
// Discord/Telegram only invited members can drive the agent, so handing
|
|
63
|
-
// every conversation the shared credentials matches who can use them.
|
|
64
|
-
// GitHub conversations are driven by repo commenters — a wider trust
|
|
65
|
-
// boundary — so they never inherit ambient credentials. Their sandboxes
|
|
66
|
-
// stay credential-free (the adapter acts through host-side scoped tokens)
|
|
67
|
-
// unless an admin explicitly provisions a vault for that conversation.
|
|
68
|
-
if (platform === "github")
|
|
69
|
-
return;
|
|
60
|
+
ensureDefaultSharedVault(vaultKey) {
|
|
70
61
|
if (this.baseConfig.type !== "image" && this.baseConfig.type !== "cloudflare")
|
|
71
62
|
return;
|
|
72
63
|
if (this.vaultManager.hasEntry(vaultKey))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execution-resolver.js","sourceRoot":"","sources":["../src/execution-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"fs\";\nimport { join } from \"path\";\nimport {\n conversationSettingsPath,\n loadGlobalSettings,\n resolveConversationSettings,\n} from \"./config.js\";\nimport { ensureDirExists, isRecord, readJsonFileIfExists } from \"./utils/file-guards.js\";\nimport { DockerContainerManager, type ContainerMount } from \"./provisioner.js\";\nimport { createExecutor, type Executor, type SandboxConfig } from \"./sandbox/index.js\";\nimport { reportUserFacingError } from \"./observability/sentry.js\";\nimport { normalizeSharedVaultName, type ResolvedVault, type VaultManager } from \"./vault/index.js\";\nimport { resolveActorVaultKey } from \"./vault/routing.js\";\nimport * as log from \"./log.js\";\n\nexport type { ActorContext, ImageWorkspaceMountMode } from \"./types.js\";\nimport type { ActorContext, ImageWorkspaceMountMode } from \"./types.js\";\n\nexport function readConversationWorkspaceMountMode(\n workspaceDir: string | undefined,\n conversationId: string,\n): ImageWorkspaceMountMode {\n const globalDefault = readGlobalWorkspaceMountMode();\n if (!workspaceDir) {\n return globalDefault;\n }\n\n const conversationDir = join(workspaceDir, conversationId);\n try {\n return resolveConversationSettings(conversationDir).sandboxImageWorkspaceMount ?? globalDefault;\n } catch (err) {\n log.logWarning(\n \"Falling back while resolving conversation workspace mount\",\n err instanceof Error ? err.message : String(err),\n );\n const raw = readConversationSettingsFallback(conversationSettingsPath(conversationDir));\n return raw?.sandbox?.image?.workspaceMount ?? globalDefault;\n }\n}\n\nfunction readGlobalWorkspaceMountMode(): ImageWorkspaceMountMode {\n try {\n return loadGlobalSettings().sandboxImageWorkspaceMount ?? \"private\";\n } catch (err) {\n log.logWarning(\n \"Using default workspace mount because global settings could not be read\",\n err instanceof Error ? err.message : String(err),\n );\n return \"private\";\n }\n}\n\nfunction readConversationSettingsFallback(\n settingsPath: string,\n): { sandbox?: { image?: { workspaceMount?: ImageWorkspaceMountMode } } } | undefined {\n try {\n return readJsonFileIfExists(\n settingsPath,\n (value): value is { sandbox?: { image?: { workspaceMount?: ImageWorkspaceMountMode } } } =>\n isRecord(value),\n () => \"Ignoring malformed conversation settings file while resolving workspace mount\",\n );\n } catch (err) {\n log.logWarning(\n \"Ignoring malformed conversation settings fallback while resolving workspace mount\",\n err instanceof Error ? err.message : String(err),\n );\n return undefined;\n }\n}\n\nexport class ActorExecutionResolver {\n private readonly ensuredConversationDirs = new Set<string>();\n\n constructor(\n private baseConfig: SandboxConfig,\n private vaultManager: VaultManager,\n private provisioner?: DockerContainerManager,\n private workspaceDir?: string,\n ) {}\n\n async resolve(context: ActorContext): Promise<Executor> {\n const vaultKey = resolveActorVaultKey(this.baseConfig, context.userId, context.conversationId);\n this.ensureDefaultSharedVault(vaultKey, context.platform);\n\n const vault = this.vaultManager.resolve(vaultKey);\n const config = this.resolveSandboxConfig(vaultKey);\n const env =\n config.type !== \"host\" && vault && Object.keys(vault.env).length > 0 ? vault.env : undefined;\n return createExecutor(\n config,\n env,\n this.buildEnsureReadyCallback(vaultKey, context.conversationId, config, vault),\n );\n }\n\n private ensureDefaultSharedVault(vaultKey: string, platform: string): void {\n // The ambient default vault is a membership-trust convenience: on Slack/\n // Discord/Telegram only invited members can drive the agent, so handing\n // every conversation the shared credentials matches who can use them.\n // GitHub conversations are driven by repo commenters — a wider trust\n // boundary — so they never inherit ambient credentials. Their sandboxes\n // stay credential-free (the adapter acts through host-side scoped tokens)\n // unless an admin explicitly provisions a vault for that conversation.\n if (platform === \"github\") return;\n if (this.baseConfig.type !== \"image\" && this.baseConfig.type !== \"cloudflare\") return;\n if (this.vaultManager.hasEntry(vaultKey)) return;\n\n let profile: string | undefined;\n try {\n profile = loadGlobalSettings().defaultSharedVault;\n } catch {\n return;\n }\n if (!profile || normalizeSharedVaultName(profile) !== profile) return;\n\n this.vaultManager.copySharedVaultTo(profile, vaultKey);\n }\n\n private resolveSandboxConfig(vaultKey: string): SandboxConfig {\n const config = this.vaultManager.getSandboxConfig(vaultKey, this.baseConfig);\n if (this.baseConfig.type !== \"image\") {\n return config;\n }\n\n if (config.type === \"container\") {\n return config;\n }\n\n return {\n type: \"container\",\n container: DockerContainerManager.containerName(vaultKey),\n };\n }\n\n private buildEnsureReadyCallback(\n vaultKey: string,\n conversationId: string,\n config: SandboxConfig,\n vault?: ResolvedVault,\n ): (() => Promise<void>) | undefined {\n if (this.baseConfig.type !== \"image\" || config.type !== \"container\") {\n return undefined;\n }\n\n return async () => {\n const expected = config.container || DockerContainerManager.containerName(vaultKey);\n let actual: string | undefined;\n try {\n actual = await this.provisioner?.provision(vaultKey, {\n containerName: expected,\n mounts: this.resolveMounts(conversationId, vault),\n conversationId,\n });\n } catch (err) {\n reportUserFacingError(err, {\n domain: \"sandbox\",\n surface: \"sandbox_provision\",\n operation: \"ensure_image_container_ready\",\n severity: \"error\",\n context: {\n sandboxType: \"image\",\n resolvedSandboxType: config.type,\n conversationId,\n vaultKey,\n expectedContainer: expected,\n hasVault: Boolean(vault),\n },\n });\n throw err;\n }\n if (actual && actual !== expected) {\n throw new Error(\n `Provisioner returned container \"${actual}\" for container key \"${vaultKey}\", expected \"${expected}\"`,\n );\n }\n };\n }\n\n private resolveMounts(conversationId: string, vault?: ResolvedVault): ContainerMount[] {\n const mountsByTarget = new Map<string, ContainerMount>();\n for (const mount of this.buildImageSandboxMounts(conversationId)) {\n mountsByTarget.set(mount.target, mount);\n }\n for (const mount of vault?.mounts ?? []) {\n if (!existsSync(mount.source)) {\n reportUserFacingError(new Error(\"Vault mount source is missing\"), {\n domain: \"sandbox\",\n surface: \"vault_injection\",\n operation: \"resolve_mounts\",\n severity: \"warning\",\n context: {\n sandboxType: \"image\",\n conversationId,\n target: mount.target,\n hasVault: Boolean(vault),\n },\n });\n continue;\n }\n mountsByTarget.set(mount.target, { source: mount.source, target: mount.target });\n }\n return [...mountsByTarget.values()];\n }\n\n private buildImageSandboxMounts(conversationId: string): ContainerMount[] {\n if (!this.workspaceDir) {\n return [];\n }\n\n if (readConversationWorkspaceMountMode(this.workspaceDir, conversationId) === \"full\") {\n return [{ source: this.workspaceDir, target: \"/workspace\" }];\n }\n\n const conversationDir = join(this.workspaceDir, conversationId);\n if (!this.ensuredConversationDirs.has(conversationId)) {\n ensureDirExists(conversationDir);\n this.ensuredConversationDirs.add(conversationId);\n }\n\n return [\n { source: join(this.workspaceDir, \"MEMORY.md\"), target: \"/workspace/MEMORY.md\" },\n { source: join(this.workspaceDir, \"skills\"), target: \"/workspace/skills\" },\n { source: join(this.workspaceDir, \"events\"), target: \"/workspace/events\" },\n { source: conversationDir, target: `/workspace/${conversationId}` },\n ];\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"execution-resolver.js","sourceRoot":"","sources":["../src/execution-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"fs\";\nimport { join } from \"path\";\nimport {\n conversationSettingsPath,\n loadGlobalSettings,\n resolveConversationSettings,\n} from \"./config.js\";\nimport { ensureDirExists, isRecord, readJsonFileIfExists } from \"./utils/file-guards.js\";\nimport { DockerContainerManager, type ContainerMount } from \"./provisioner.js\";\nimport { createExecutor, type Executor, type SandboxConfig } from \"./sandbox/index.js\";\nimport { reportUserFacingError } from \"./observability/sentry.js\";\nimport { normalizeSharedVaultName, type ResolvedVault, type VaultManager } from \"./vault/index.js\";\nimport { resolveActorVaultKey } from \"./vault/routing.js\";\nimport * as log from \"./log.js\";\n\nexport type { ActorContext, ImageWorkspaceMountMode } from \"./types.js\";\nimport type { ActorContext, ImageWorkspaceMountMode } from \"./types.js\";\n\nexport function readConversationWorkspaceMountMode(\n workspaceDir: string | undefined,\n conversationId: string,\n): ImageWorkspaceMountMode {\n const globalDefault = readGlobalWorkspaceMountMode();\n if (!workspaceDir) {\n return globalDefault;\n }\n\n const conversationDir = join(workspaceDir, conversationId);\n try {\n return resolveConversationSettings(conversationDir).sandboxImageWorkspaceMount ?? globalDefault;\n } catch (err) {\n log.logWarning(\n \"Falling back while resolving conversation workspace mount\",\n err instanceof Error ? err.message : String(err),\n );\n const raw = readConversationSettingsFallback(conversationSettingsPath(conversationDir));\n return raw?.sandbox?.image?.workspaceMount ?? globalDefault;\n }\n}\n\nfunction readGlobalWorkspaceMountMode(): ImageWorkspaceMountMode {\n try {\n return loadGlobalSettings().sandboxImageWorkspaceMount ?? \"private\";\n } catch (err) {\n log.logWarning(\n \"Using default workspace mount because global settings could not be read\",\n err instanceof Error ? err.message : String(err),\n );\n return \"private\";\n }\n}\n\nfunction readConversationSettingsFallback(\n settingsPath: string,\n): { sandbox?: { image?: { workspaceMount?: ImageWorkspaceMountMode } } } | undefined {\n try {\n return readJsonFileIfExists(\n settingsPath,\n (value): value is { sandbox?: { image?: { workspaceMount?: ImageWorkspaceMountMode } } } =>\n isRecord(value),\n () => \"Ignoring malformed conversation settings file while resolving workspace mount\",\n );\n } catch (err) {\n log.logWarning(\n \"Ignoring malformed conversation settings fallback while resolving workspace mount\",\n err instanceof Error ? err.message : String(err),\n );\n return undefined;\n }\n}\n\nexport class ActorExecutionResolver {\n private readonly ensuredConversationDirs = new Set<string>();\n\n constructor(\n private baseConfig: SandboxConfig,\n private vaultManager: VaultManager,\n private provisioner?: DockerContainerManager,\n private workspaceDir?: string,\n ) {}\n\n async resolve(context: ActorContext): Promise<Executor> {\n const vaultKey = resolveActorVaultKey(this.baseConfig, context.userId, context.conversationId);\n this.ensureDefaultSharedVault(vaultKey);\n\n const vault = this.vaultManager.resolve(vaultKey);\n const config = this.resolveSandboxConfig(vaultKey);\n const env =\n config.type !== \"host\" && vault && Object.keys(vault.env).length > 0 ? vault.env : undefined;\n return createExecutor(\n config,\n env,\n this.buildEnsureReadyCallback(vaultKey, context.conversationId, config, vault),\n );\n }\n\n private ensureDefaultSharedVault(vaultKey: string): void {\n if (this.baseConfig.type !== \"image\" && this.baseConfig.type !== \"cloudflare\") return;\n if (this.vaultManager.hasEntry(vaultKey)) return;\n\n let profile: string | undefined;\n try {\n profile = loadGlobalSettings().defaultSharedVault;\n } catch {\n return;\n }\n if (!profile || normalizeSharedVaultName(profile) !== profile) return;\n\n this.vaultManager.copySharedVaultTo(profile, vaultKey);\n }\n\n private resolveSandboxConfig(vaultKey: string): SandboxConfig {\n const config = this.vaultManager.getSandboxConfig(vaultKey, this.baseConfig);\n if (this.baseConfig.type !== \"image\") {\n return config;\n }\n\n if (config.type === \"container\") {\n return config;\n }\n\n return {\n type: \"container\",\n container: DockerContainerManager.containerName(vaultKey),\n };\n }\n\n private buildEnsureReadyCallback(\n vaultKey: string,\n conversationId: string,\n config: SandboxConfig,\n vault?: ResolvedVault,\n ): (() => Promise<void>) | undefined {\n if (this.baseConfig.type !== \"image\" || config.type !== \"container\") {\n return undefined;\n }\n\n return async () => {\n const expected = config.container || DockerContainerManager.containerName(vaultKey);\n let actual: string | undefined;\n try {\n actual = await this.provisioner?.provision(vaultKey, {\n containerName: expected,\n mounts: this.resolveMounts(conversationId, vault),\n conversationId,\n });\n } catch (err) {\n reportUserFacingError(err, {\n domain: \"sandbox\",\n surface: \"sandbox_provision\",\n operation: \"ensure_image_container_ready\",\n severity: \"error\",\n context: {\n sandboxType: \"image\",\n resolvedSandboxType: config.type,\n conversationId,\n vaultKey,\n expectedContainer: expected,\n hasVault: Boolean(vault),\n },\n });\n throw err;\n }\n if (actual && actual !== expected) {\n throw new Error(\n `Provisioner returned container \"${actual}\" for container key \"${vaultKey}\", expected \"${expected}\"`,\n );\n }\n };\n }\n\n private resolveMounts(conversationId: string, vault?: ResolvedVault): ContainerMount[] {\n const mountsByTarget = new Map<string, ContainerMount>();\n for (const mount of this.buildImageSandboxMounts(conversationId)) {\n mountsByTarget.set(mount.target, mount);\n }\n for (const mount of vault?.mounts ?? []) {\n if (!existsSync(mount.source)) {\n reportUserFacingError(new Error(\"Vault mount source is missing\"), {\n domain: \"sandbox\",\n surface: \"vault_injection\",\n operation: \"resolve_mounts\",\n severity: \"warning\",\n context: {\n sandboxType: \"image\",\n conversationId,\n target: mount.target,\n hasVault: Boolean(vault),\n },\n });\n continue;\n }\n mountsByTarget.set(mount.target, { source: mount.source, target: mount.target });\n }\n return [...mountsByTarget.values()];\n }\n\n private buildImageSandboxMounts(conversationId: string): ContainerMount[] {\n if (!this.workspaceDir) {\n return [];\n }\n\n if (readConversationWorkspaceMountMode(this.workspaceDir, conversationId) === \"full\") {\n return [{ source: this.workspaceDir, target: \"/workspace\" }];\n }\n\n const conversationDir = join(this.workspaceDir, conversationId);\n if (!this.ensuredConversationDirs.has(conversationId)) {\n ensureDirExists(conversationDir);\n this.ensuredConversationDirs.add(conversationId);\n }\n\n return [\n { source: join(this.workspaceDir, \"MEMORY.md\"), target: \"/workspace/MEMORY.md\" },\n { source: join(this.workspaceDir, \"skills\"), target: \"/workspace/skills\" },\n { source: join(this.workspaceDir, \"events\"), target: \"/workspace/events\" },\n { source: conversationDir, target: `/workspace/${conversationId}` },\n ];\n }\n}\n"]}
|
|
@@ -4,11 +4,11 @@ import { type MikanSkill } from "../skills.js";
|
|
|
4
4
|
import { ExtensionRegistry } from "./registry.js";
|
|
5
5
|
import type { ExtensionHostServices, ExtensionLoadError, LoadedExtension } from "./types.js";
|
|
6
6
|
/**
|
|
7
|
-
* Canonical host-only extension
|
|
8
|
-
*
|
|
9
|
-
* `<stateDir>/
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* Canonical host-only extension directories for a conversation:
|
|
8
|
+
* `<stateDir>/extensions/global` (all conversations) and
|
|
9
|
+
* `<stateDir>/extensions/<conversationId>` (that conversation only).
|
|
10
|
+
* `global` is reserved as a scope name; platform conversation ids never
|
|
11
|
+
* take that value.
|
|
12
12
|
*/
|
|
13
13
|
export declare function defaultExtensionDirs(conversationId: string, stateDir?: string): string[];
|
|
14
14
|
/** Discovery-only view of an installed extension (module is not imported). */
|
|
@@ -51,36 +51,12 @@ export interface LoadExtensionsResult {
|
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
53
|
* Filesystem-safe identifier for an extension, derived from its install
|
|
54
|
-
* location
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
54
|
+
* location (directory name for `<name>/index.mjs`, file basename otherwise).
|
|
55
|
+
* The slug keys the extension's data dir, secrets vault, and schedule
|
|
56
|
+
* ownership — installing the same extension globally and per-conversation
|
|
57
|
+
* shares one slug and therefore one data dir.
|
|
58
58
|
*/
|
|
59
59
|
export declare function extensionSlug(entrypoint: string): string;
|
|
60
60
|
/** Load and activate all extensions found in the given directories. */
|
|
61
61
|
export declare function loadExtensions(options: LoadExtensionsOptions): Promise<LoadExtensionsResult>;
|
|
62
|
-
export interface ExtensionValidation {
|
|
63
|
-
ok: boolean;
|
|
64
|
-
/** Slug the extension would install as (from the source dir/file name). */
|
|
65
|
-
slug: string;
|
|
66
|
-
/** Display name (manifest name, else slug). */
|
|
67
|
-
name: string;
|
|
68
|
-
version?: string;
|
|
69
|
-
description?: string;
|
|
70
|
-
/** Resolved entrypoint that would be imported. */
|
|
71
|
-
entrypoint?: string;
|
|
72
|
-
/** Skill names shipped alongside the extension. */
|
|
73
|
-
skillNames: string[];
|
|
74
|
-
errors: string[];
|
|
75
|
-
warnings: string[];
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Validate an extension source path (a directory or a single file) WITHOUT
|
|
79
|
-
* activating it: resolves the entrypoint (package.json `mikan.extensions`,
|
|
80
|
-
* index file, or the file itself), imports it, and checks it exports an
|
|
81
|
-
* `activate` function. Used by `mikan ext install` as a preflight and by
|
|
82
|
-
* `mikan ext validate`. Importing runs top-level module code but not
|
|
83
|
-
* `activate`, so there are no schedule/tool side effects.
|
|
84
|
-
*/
|
|
85
|
-
export declare function validateExtension(sourcePath: string): Promise<ExtensionValidation>;
|
|
86
62
|
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/harness/extensions/loader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/harness/extensions/loader.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAa,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EACV,qBAAqB,EACrB,kBAAkB,EAKlB,eAAe,EAIhB,MAAM,YAAY,CAAC;AAIpB;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,cAAc,EAAE,MAAM,EACtB,QAAQ,GAAE,MAAkC,GAC3C,MAAM,EAAE,CAEV;AAED,8EAA8E;AAC9E,MAAM,WAAW,sBAAsB;IACrC,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,sBAAsB,EAAE,CAkBhF;AAED,MAAM,WAAW,qBAAqB;IACpC,2EAA2E;IAC3E,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE;QACP,cAAc,EAAE,MAAM,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;QACrB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QAClB,aAAa,EAAE,aAAa,CAAC;KAC9B,CAAC;IACF,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CAClC;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,4EAA4E;IAC5E,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AA+BD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAUxD;AAkMD,uEAAuE;AACvE,wBAAsB,cAAc,CAClC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAmD/B"}
|
|
@@ -12,48 +12,31 @@
|
|
|
12
12
|
* under mikan's state dir.
|
|
13
13
|
*
|
|
14
14
|
* Directories are scanned in order; all discovered extensions activate.
|
|
15
|
-
* Accepted layouts
|
|
15
|
+
* Accepted layouts:
|
|
16
16
|
*
|
|
17
|
-
* - `extensions/<name
|
|
18
|
-
* - `extensions/<name>/index
|
|
19
|
-
* - `extensions/<name>/package.json` (mikan.extensions entrypoint;
|
|
20
|
-
* may carry npm dependencies in node_modules)
|
|
17
|
+
* - `extensions/<name>.mjs` / `extensions/<name>.js`
|
|
18
|
+
* - `extensions/<name>/index.mjs` / `extensions/<name>/index.js`
|
|
21
19
|
*
|
|
22
|
-
* Modules are imported with a
|
|
23
|
-
*
|
|
20
|
+
* Modules are imported with a cache-busting query so edited extensions are
|
|
21
|
+
* picked up when a new harness instance is created for a conversation.
|
|
24
22
|
*/
|
|
25
23
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync } from "fs";
|
|
26
24
|
import { homedir } from "os";
|
|
27
25
|
import { basename, dirname, join } from "path";
|
|
28
|
-
import {
|
|
26
|
+
import { pathToFileURL } from "url";
|
|
29
27
|
import * as log from "../../log.js";
|
|
30
28
|
import { loadSkillsFromDir } from "../skills.js";
|
|
31
29
|
import { ExtensionRegistry } from "./registry.js";
|
|
32
|
-
const EXTENSION_FILE_PATTERN = /\.(mjs|js
|
|
33
|
-
const INDEX_FILE_PATTERN = /^index\.(mjs|js|ts|mts)$/;
|
|
34
|
-
const INDEX_CANDIDATES = ["index.mjs", "index.js", "index.ts", "index.mts"];
|
|
30
|
+
const EXTENSION_FILE_PATTERN = /\.(mjs|js)$/;
|
|
35
31
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
|
|
41
|
-
async function importExtensionModule(entrypoint) {
|
|
42
|
-
const jiti = createJiti(import.meta.url, { moduleCache: false, fsCache: false });
|
|
43
|
-
return jiti.import(entrypoint);
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Canonical host-only extension code directories for a conversation, in load
|
|
47
|
-
* order: `<stateDir>/global/extensions` (all conversations) then
|
|
48
|
-
* `<stateDir>/conversations/<conversationId>/extensions` (that conversation
|
|
49
|
-
* only). `global` and `conversations` are the two sibling scopes; see
|
|
50
|
-
* `LAYOUT.md`. Conversation ids are used verbatim (see LAYOUT.md § Casing).
|
|
32
|
+
* Canonical host-only extension directories for a conversation:
|
|
33
|
+
* `<stateDir>/extensions/global` (all conversations) and
|
|
34
|
+
* `<stateDir>/extensions/<conversationId>` (that conversation only).
|
|
35
|
+
* `global` is reserved as a scope name; platform conversation ids never
|
|
36
|
+
* take that value.
|
|
51
37
|
*/
|
|
52
38
|
export function defaultExtensionDirs(conversationId, stateDir = join(homedir(), ".mikan")) {
|
|
53
|
-
return [
|
|
54
|
-
join(stateDir, "global", "extensions"),
|
|
55
|
-
join(stateDir, "conversations", conversationId, "extensions"),
|
|
56
|
-
];
|
|
39
|
+
return [join(stateDir, "extensions", "global"), join(stateDir, "extensions", conversationId)];
|
|
57
40
|
}
|
|
58
41
|
/**
|
|
59
42
|
* List installed extensions without importing or activating them — safe for
|
|
@@ -63,9 +46,9 @@ export function defaultExtensionDirs(conversationId, stateDir = join(homedir(),
|
|
|
63
46
|
export function listInstalledExtensions(dirs) {
|
|
64
47
|
const infos = [];
|
|
65
48
|
for (const dir of dirs) {
|
|
66
|
-
for (const
|
|
67
|
-
const slug =
|
|
68
|
-
const manifest = readManifest(
|
|
49
|
+
for (const entrypoint of discoverExtensionEntrypoints(dir)) {
|
|
50
|
+
const slug = extensionSlug(entrypoint);
|
|
51
|
+
const manifest = readManifest(entrypoint);
|
|
69
52
|
infos.push({
|
|
70
53
|
name: manifest.name ?? slug,
|
|
71
54
|
slug,
|
|
@@ -73,7 +56,7 @@ export function listInstalledExtensions(dirs) {
|
|
|
73
56
|
dir,
|
|
74
57
|
version: manifest.version,
|
|
75
58
|
description: manifest.description,
|
|
76
|
-
skillNames: loadExtensionSkills(
|
|
59
|
+
skillNames: loadExtensionSkills(entrypoint, slug).map((skill) => skill.name),
|
|
77
60
|
});
|
|
78
61
|
}
|
|
79
62
|
}
|
|
@@ -82,18 +65,10 @@ export function listInstalledExtensions(dirs) {
|
|
|
82
65
|
function discoverExtensionEntrypoints(dir) {
|
|
83
66
|
if (!existsSync(dir))
|
|
84
67
|
return [];
|
|
85
|
-
const
|
|
68
|
+
const entrypoints = [];
|
|
86
69
|
for (const name of readdirSync(dir).toSorted()) {
|
|
87
70
|
if (name.startsWith("."))
|
|
88
71
|
continue;
|
|
89
|
-
if (INDEX_FILE_PATTERN.test(name)) {
|
|
90
|
-
// An index file at the scan root means the extension's contents were
|
|
91
|
-
// copied into the scope directory itself; the slug would degenerate to
|
|
92
|
-
// the scope name (e.g. the conversation id), mis-keying its data dir,
|
|
93
|
-
// secrets, and schedules. Require a named form instead.
|
|
94
|
-
log.logWarning(`Ignoring extension index file at scope root: ${join(dir, name)}`, "install into a named subdirectory (e.g. extensions/<scope>/my-ext/index.mjs)");
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
72
|
const fullPath = join(dir, name);
|
|
98
73
|
let stats;
|
|
99
74
|
try {
|
|
@@ -103,73 +78,32 @@ function discoverExtensionEntrypoints(dir) {
|
|
|
103
78
|
continue;
|
|
104
79
|
}
|
|
105
80
|
if (stats.isFile() && EXTENSION_FILE_PATTERN.test(name)) {
|
|
106
|
-
|
|
107
|
-
found.push({ entrypoint: fullPath, rootDir: fullPath });
|
|
81
|
+
entrypoints.push(fullPath);
|
|
108
82
|
continue;
|
|
109
83
|
}
|
|
110
84
|
if (stats.isDirectory()) {
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
85
|
+
for (const candidate of ["index.mjs", "index.js"]) {
|
|
86
|
+
const indexPath = join(fullPath, candidate);
|
|
87
|
+
if (existsSync(indexPath)) {
|
|
88
|
+
entrypoints.push(indexPath);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
114
92
|
}
|
|
115
93
|
}
|
|
116
|
-
return
|
|
94
|
+
return entrypoints;
|
|
117
95
|
}
|
|
118
96
|
/**
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
97
|
+
* Filesystem-safe identifier for an extension, derived from its install
|
|
98
|
+
* location (directory name for `<name>/index.mjs`, file basename otherwise).
|
|
99
|
+
* The slug keys the extension's data dir, secrets vault, and schedule
|
|
100
|
+
* ownership — installing the same extension globally and per-conversation
|
|
101
|
+
* shares one slug and therefore one data dir.
|
|
124
102
|
*/
|
|
125
|
-
function
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (existsSync(resolved))
|
|
130
|
-
return resolved;
|
|
131
|
-
log.logWarning(`Extension package.json declares a missing entrypoint: ${resolved}`, "falling back to an index file");
|
|
132
|
-
}
|
|
133
|
-
for (const candidate of INDEX_CANDIDATES) {
|
|
134
|
-
const indexPath = join(dir, candidate);
|
|
135
|
-
if (existsSync(indexPath))
|
|
136
|
-
return indexPath;
|
|
137
|
-
}
|
|
138
|
-
return undefined;
|
|
139
|
-
}
|
|
140
|
-
/** Read and shape a directory's package.json; undefined when absent/malformed. */
|
|
141
|
-
function readPackageJson(dir) {
|
|
142
|
-
const packageJsonPath = join(dir, "package.json");
|
|
143
|
-
if (!existsSync(packageJsonPath))
|
|
144
|
-
return undefined;
|
|
145
|
-
try {
|
|
146
|
-
const parsed = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
147
|
-
if (!parsed || typeof parsed !== "object")
|
|
148
|
-
return undefined;
|
|
149
|
-
const record = parsed;
|
|
150
|
-
const mikanRaw = record.mikan;
|
|
151
|
-
return {
|
|
152
|
-
name: typeof record.name === "string" ? record.name : undefined,
|
|
153
|
-
version: typeof record.version === "string" ? record.version : undefined,
|
|
154
|
-
description: typeof record.description === "string" ? record.description : undefined,
|
|
155
|
-
mikan: {
|
|
156
|
-
extensions: Array.isArray(mikanRaw?.extensions)
|
|
157
|
-
? mikanRaw.extensions.filter((entry) => typeof entry === "string")
|
|
158
|
-
: undefined,
|
|
159
|
-
displayName: typeof mikanRaw?.displayName === "string" ? mikanRaw.displayName : undefined,
|
|
160
|
-
},
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
catch (err) {
|
|
164
|
-
log.logWarning(`Ignoring malformed package.json: ${packageJsonPath}`, String(err));
|
|
165
|
-
return undefined;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
/** Read the `mikan.extensions` entrypoint list from a directory's package.json. */
|
|
169
|
-
function readMikanManifestEntrypoints(dir) {
|
|
170
|
-
return readPackageJson(dir)?.mikan?.extensions ?? [];
|
|
171
|
-
}
|
|
172
|
-
function sanitizeSlug(base) {
|
|
103
|
+
export function extensionSlug(entrypoint) {
|
|
104
|
+
const base = /^index\.(mjs|js)$/.test(basename(entrypoint))
|
|
105
|
+
? basename(dirname(entrypoint))
|
|
106
|
+
: basename(entrypoint).replace(EXTENSION_FILE_PATTERN, "");
|
|
173
107
|
const slug = base
|
|
174
108
|
.toLowerCase()
|
|
175
109
|
.replace(/[^a-z0-9_-]+/g, "-")
|
|
@@ -177,28 +111,6 @@ function sanitizeSlug(base) {
|
|
|
177
111
|
.slice(0, 64);
|
|
178
112
|
return slug || "extension";
|
|
179
113
|
}
|
|
180
|
-
/**
|
|
181
|
-
* Filesystem-safe identifier for an extension, derived from its install
|
|
182
|
-
* location: the directory name for a directory-form extension, or the file
|
|
183
|
-
* basename for a bare file. The slug keys the extension's data dir, secrets
|
|
184
|
-
* vault, and schedule ownership — installing the same extension globally and
|
|
185
|
-
* per-conversation shares one slug and therefore one shared data dir.
|
|
186
|
-
*/
|
|
187
|
-
export function extensionSlug(entrypoint) {
|
|
188
|
-
// Bare-file form (rootDir === entrypoint, a *.mjs/.ts file) → file basename.
|
|
189
|
-
// Directory form → directory name. Detect the directory form structurally:
|
|
190
|
-
// an entrypoint whose parent holds a package.json or is an index file.
|
|
191
|
-
if (INDEX_FILE_PATTERN.test(basename(entrypoint))) {
|
|
192
|
-
return sanitizeSlug(basename(dirname(entrypoint)));
|
|
193
|
-
}
|
|
194
|
-
return sanitizeSlug(basename(entrypoint).replace(EXTENSION_FILE_PATTERN, ""));
|
|
195
|
-
}
|
|
196
|
-
/** Slug from a discovered extension's root dir (directory or bare file path). */
|
|
197
|
-
function slugForRoot(rootDir) {
|
|
198
|
-
return EXTENSION_FILE_PATTERN.test(basename(rootDir))
|
|
199
|
-
? sanitizeSlug(basename(rootDir).replace(EXTENSION_FILE_PATTERN, ""))
|
|
200
|
-
: sanitizeSlug(basename(rootDir));
|
|
201
|
-
}
|
|
202
114
|
/** Sanitize an extension-chosen schedule name into a filename segment. */
|
|
203
115
|
function scheduleNameSegment(name) {
|
|
204
116
|
const segment = name
|
|
@@ -210,27 +122,11 @@ function scheduleNameSegment(name) {
|
|
|
210
122
|
throw new Error(`Invalid schedule name: ${JSON.stringify(name)}`);
|
|
211
123
|
return segment;
|
|
212
124
|
}
|
|
213
|
-
/**
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
* `version`, `description`, plus an optional `mikan.displayName` that
|
|
217
|
-
* overrides the npm name for the user-facing label (npm names are
|
|
218
|
-
* lowercase/scoped; display names can be arbitrary). A `manifest.json` is a
|
|
219
|
-
* fallback for extensions that ship no package.json.
|
|
220
|
-
*/
|
|
221
|
-
function readManifest(rootDir) {
|
|
222
|
-
// Bare-file extensions (rootDir is a *.mjs/.ts file) have no manifest.
|
|
223
|
-
if (EXTENSION_FILE_PATTERN.test(basename(rootDir)))
|
|
125
|
+
/** Read `manifest.json` next to a directory-form extension's entrypoint. */
|
|
126
|
+
function readManifest(entrypoint) {
|
|
127
|
+
if (!/^index\.(mjs|js)$/.test(basename(entrypoint)))
|
|
224
128
|
return {};
|
|
225
|
-
const
|
|
226
|
-
if (pkg && (pkg.mikan?.displayName ?? pkg.name ?? pkg.version ?? pkg.description)) {
|
|
227
|
-
return {
|
|
228
|
-
name: pkg.mikan?.displayName ?? pkg.name,
|
|
229
|
-
version: pkg.version,
|
|
230
|
-
description: pkg.description,
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
const manifestPath = join(rootDir, "manifest.json");
|
|
129
|
+
const manifestPath = join(dirname(entrypoint), "manifest.json");
|
|
234
130
|
if (!existsSync(manifestPath))
|
|
235
131
|
return {};
|
|
236
132
|
try {
|
|
@@ -254,11 +150,10 @@ function readManifest(rootDir) {
|
|
|
254
150
|
* Marked inline: extension files live under the host-only state dir, which
|
|
255
151
|
* sandbox containers cannot read, so skill bodies must ride in the prompt.
|
|
256
152
|
*/
|
|
257
|
-
function loadExtensionSkills(
|
|
258
|
-
|
|
259
|
-
if (EXTENSION_FILE_PATTERN.test(basename(rootDir)))
|
|
153
|
+
function loadExtensionSkills(entrypoint, slug) {
|
|
154
|
+
if (!/^index\.(mjs|js)$/.test(basename(entrypoint)))
|
|
260
155
|
return [];
|
|
261
|
-
const skillsDir = join(
|
|
156
|
+
const skillsDir = join(dirname(entrypoint), "skills");
|
|
262
157
|
if (!existsSync(skillsDir))
|
|
263
158
|
return [];
|
|
264
159
|
const result = loadSkillsFromDir({ dir: skillsDir, source: `extension:${slug}` });
|
|
@@ -332,15 +227,7 @@ function buildExtensionApi(params) {
|
|
|
332
227
|
context,
|
|
333
228
|
paths: {
|
|
334
229
|
get dataDir() {
|
|
335
|
-
|
|
336
|
-
// other host-only assets; removed when the conversation is deleted.
|
|
337
|
-
const dir = join(stateDir, "conversations", conversationId, "extension-data", slug);
|
|
338
|
-
mkdirSync(dir, { recursive: true });
|
|
339
|
-
return dir;
|
|
340
|
-
},
|
|
341
|
-
get sharedDataDir() {
|
|
342
|
-
// Cross-conversation data under the sibling `global` scope.
|
|
343
|
-
const dir = join(stateDir, "global", "extension-data", slug);
|
|
230
|
+
const dir = join(stateDir, "extension-data", slug);
|
|
344
231
|
mkdirSync(dir, { recursive: true });
|
|
345
232
|
return dir;
|
|
346
233
|
},
|
|
@@ -383,12 +270,6 @@ function buildExtensionApi(params) {
|
|
|
383
270
|
}
|
|
384
271
|
await services.postMessage(conversationId, text);
|
|
385
272
|
},
|
|
386
|
-
react: async (messageTs, emoji) => {
|
|
387
|
-
if (!services.addReaction) {
|
|
388
|
-
throw new Error("api.react is unavailable: this context provides no reaction support");
|
|
389
|
-
}
|
|
390
|
-
await services.addReaction(conversationId, messageTs, emoji);
|
|
391
|
-
},
|
|
392
273
|
};
|
|
393
274
|
}
|
|
394
275
|
function resolveActivate(moduleExports) {
|
|
@@ -421,9 +302,10 @@ export async function loadExtensions(options) {
|
|
|
421
302
|
const skills = [];
|
|
422
303
|
const services = options.services ?? {};
|
|
423
304
|
for (const dir of options.dirs) {
|
|
424
|
-
for (const
|
|
305
|
+
for (const entrypoint of discoverExtensionEntrypoints(dir)) {
|
|
425
306
|
try {
|
|
426
|
-
const
|
|
307
|
+
const moduleUrl = `${pathToFileURL(entrypoint).href}?t=${Date.now()}`;
|
|
308
|
+
const moduleExports = await import(moduleUrl);
|
|
427
309
|
const extension = resolveActivate(moduleExports);
|
|
428
310
|
if (!extension) {
|
|
429
311
|
errors.push({
|
|
@@ -432,8 +314,8 @@ export async function loadExtensions(options) {
|
|
|
432
314
|
});
|
|
433
315
|
continue;
|
|
434
316
|
}
|
|
435
|
-
const slug =
|
|
436
|
-
const manifest = readManifest(
|
|
317
|
+
const slug = extensionSlug(entrypoint);
|
|
318
|
+
const manifest = readManifest(entrypoint);
|
|
437
319
|
const name = manifest.name ?? extension.name ?? slug;
|
|
438
320
|
const api = buildExtensionApi({
|
|
439
321
|
name,
|
|
@@ -443,7 +325,7 @@ export async function loadExtensions(options) {
|
|
|
443
325
|
services,
|
|
444
326
|
});
|
|
445
327
|
await extension.activate(api);
|
|
446
|
-
const extensionSkills = loadExtensionSkills(
|
|
328
|
+
const extensionSkills = loadExtensionSkills(entrypoint, slug);
|
|
447
329
|
skills.push(...extensionSkills);
|
|
448
330
|
extensions.push({
|
|
449
331
|
name,
|
|
@@ -464,62 +346,4 @@ export async function loadExtensions(options) {
|
|
|
464
346
|
}
|
|
465
347
|
return { registry, extensions, errors, skills };
|
|
466
348
|
}
|
|
467
|
-
/**
|
|
468
|
-
* Validate an extension source path (a directory or a single file) WITHOUT
|
|
469
|
-
* activating it: resolves the entrypoint (package.json `mikan.extensions`,
|
|
470
|
-
* index file, or the file itself), imports it, and checks it exports an
|
|
471
|
-
* `activate` function. Used by `mikan ext install` as a preflight and by
|
|
472
|
-
* `mikan ext validate`. Importing runs top-level module code but not
|
|
473
|
-
* `activate`, so there are no schedule/tool side effects.
|
|
474
|
-
*/
|
|
475
|
-
export async function validateExtension(sourcePath) {
|
|
476
|
-
const errors = [];
|
|
477
|
-
const warnings = [];
|
|
478
|
-
const rootDir = sourcePath.replace(/[/\\]+$/, "");
|
|
479
|
-
const base = basename(rootDir);
|
|
480
|
-
if (INDEX_FILE_PATTERN.test(base)) {
|
|
481
|
-
warnings.push("Entry is a bare index file; install into a named directory so the slug is stable.");
|
|
482
|
-
}
|
|
483
|
-
let entrypoint;
|
|
484
|
-
if (!existsSync(rootDir)) {
|
|
485
|
-
errors.push(`Path does not exist: ${rootDir}`);
|
|
486
|
-
}
|
|
487
|
-
else if (statSync(rootDir).isDirectory()) {
|
|
488
|
-
entrypoint = resolveDirectoryEntrypoint(rootDir);
|
|
489
|
-
if (!entrypoint) {
|
|
490
|
-
errors.push("No entrypoint found: expected package.json with a mikan.extensions entry, or an index.{mjs,js,ts,mts}.");
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
else if (EXTENSION_FILE_PATTERN.test(base)) {
|
|
494
|
-
entrypoint = rootDir;
|
|
495
|
-
}
|
|
496
|
-
else {
|
|
497
|
-
errors.push(`Not an extension file (expected .mjs/.js/.ts/.mts): ${rootDir}`);
|
|
498
|
-
}
|
|
499
|
-
const slug = slugForRoot(rootDir);
|
|
500
|
-
const manifest = readManifest(rootDir);
|
|
501
|
-
const skillNames = loadExtensionSkills(rootDir, slug).map((skill) => skill.name);
|
|
502
|
-
if (entrypoint) {
|
|
503
|
-
try {
|
|
504
|
-
const moduleExports = await importExtensionModule(entrypoint);
|
|
505
|
-
if (!resolveActivate(moduleExports)) {
|
|
506
|
-
errors.push("Module does not export an activate function (default or named).");
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
catch (err) {
|
|
510
|
-
errors.push(`Failed to import: ${err instanceof Error ? err.message : String(err)}`);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
return {
|
|
514
|
-
ok: errors.length === 0,
|
|
515
|
-
slug,
|
|
516
|
-
name: manifest.name ?? slug,
|
|
517
|
-
version: manifest.version,
|
|
518
|
-
description: manifest.description,
|
|
519
|
-
entrypoint,
|
|
520
|
-
skillNames,
|
|
521
|
-
errors,
|
|
522
|
-
warnings,
|
|
523
|
-
};
|
|
524
|
-
}
|
|
525
349
|
//# sourceMappingURL=loader.js.map
|