@geminixiang/mikan 1.3.0-beta.1 → 1.4.0-beta.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/CHANGELOG.md +38 -10
- package/README.md +7 -8
- package/dist/adapters/commands/login.js +1 -1
- package/dist/adapters/commands/login.js.map +1 -1
- package/dist/adapters/commands/sandbox.js +2 -2
- package/dist/adapters/commands/sandbox.js.map +1 -1
- package/dist/cli/boot.js +1 -1
- package/dist/cli/boot.js.map +1 -1
- package/dist/cli/migrate.d.ts.map +1 -1
- package/dist/cli/migrate.js +3 -2
- package/dist/cli/migrate.js.map +1 -1
- package/dist/cli/onboard.d.ts.map +1 -1
- package/dist/cli/onboard.js +15 -13
- package/dist/cli/onboard.js.map +1 -1
- package/dist/cli/pm2-ecosystem.d.ts +1 -2
- package/dist/cli/pm2-ecosystem.d.ts.map +1 -1
- package/dist/cli/pm2-ecosystem.js +2 -3
- package/dist/cli/pm2-ecosystem.js.map +1 -1
- package/dist/env-manifest.d.ts.map +1 -1
- package/dist/env-manifest.js +11 -0
- package/dist/env-manifest.js.map +1 -1
- package/dist/harness/execution-resolver.d.ts +3 -2
- package/dist/harness/execution-resolver.d.ts.map +1 -1
- package/dist/harness/execution-resolver.js +12 -10
- package/dist/harness/execution-resolver.js.map +1 -1
- package/dist/harness/prompt.d.ts.map +1 -1
- package/dist/harness/prompt.js +3 -2
- package/dist/harness/prompt.js.map +1 -1
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +37 -62
- package/dist/harness/runner.js.map +1 -1
- package/dist/harness/session.js +1 -1
- package/dist/harness/session.js.map +1 -1
- package/dist/harness/tools/attach.d.ts +3 -2
- package/dist/harness/tools/attach.d.ts.map +1 -1
- package/dist/harness/tools/attach.js +9 -2
- package/dist/harness/tools/attach.js.map +1 -1
- package/dist/harness/tools/index.d.ts +3 -2
- package/dist/harness/tools/index.d.ts.map +1 -1
- package/dist/harness/tools/index.js +6 -4
- package/dist/harness/tools/index.js.map +1 -1
- package/dist/harness/tools/jev-browser.d.ts +2 -2
- package/dist/harness/tools/jev-browser.d.ts.map +1 -1
- package/dist/harness/tools/jev-browser.js +41 -33
- package/dist/harness/tools/jev-browser.js.map +1 -1
- package/dist/harness/tools/sandbox.js +2 -2
- package/dist/harness/tools/sandbox.js.map +1 -1
- package/dist/harness/tools/types.d.ts +4 -0
- package/dist/harness/tools/types.d.ts.map +1 -1
- package/dist/harness/tools/types.js.map +1 -1
- package/dist/harness/tools/web-fetch.d.ts +6 -0
- package/dist/harness/tools/web-fetch.d.ts.map +1 -0
- package/dist/harness/tools/web-fetch.js +136 -0
- package/dist/harness/tools/web-fetch.js.map +1 -0
- package/dist/harness/types.d.ts +4 -3
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/main.js +10 -24
- package/dist/main.js.map +1 -1
- package/dist/migrations/index.js +2 -2
- package/dist/migrations/index.js.map +1 -1
- package/dist/package-version.d.ts +2 -0
- package/dist/package-version.d.ts.map +1 -0
- package/dist/package-version.js +19 -0
- package/dist/package-version.js.map +1 -0
- package/dist/sandbox/container.d.ts +3 -17
- package/dist/sandbox/container.d.ts.map +1 -1
- package/dist/sandbox/container.js +108 -129
- package/dist/sandbox/container.js.map +1 -1
- package/dist/sandbox/engine.d.ts +4 -0
- package/dist/sandbox/engine.d.ts.map +1 -0
- package/dist/sandbox/engine.js +30 -0
- package/dist/sandbox/engine.js.map +1 -0
- package/dist/sandbox/host.d.ts +1 -10
- package/dist/sandbox/host.d.ts.map +1 -1
- package/dist/sandbox/host.js +2 -79
- package/dist/sandbox/host.js.map +1 -1
- package/dist/sandbox/provisioner.d.ts +15 -1
- package/dist/sandbox/provisioner.d.ts.map +1 -1
- package/dist/sandbox/provisioner.js +159 -111
- package/dist/sandbox/provisioner.js.map +1 -1
- package/dist/sandbox/published-image.d.ts +3 -0
- package/dist/sandbox/published-image.d.ts.map +1 -0
- package/dist/sandbox/published-image.js +9 -0
- package/dist/sandbox/published-image.js.map +1 -0
- package/dist/sandbox/registry.d.ts +5 -3
- package/dist/sandbox/registry.d.ts.map +1 -1
- package/dist/sandbox/registry.js +36 -27
- package/dist/sandbox/registry.js.map +1 -1
- package/dist/sandbox/types.d.ts +14 -22
- package/dist/sandbox/types.d.ts.map +1 -1
- package/dist/sandbox/types.js.map +1 -1
- package/dist/sandbox/utils.d.ts +0 -15
- package/dist/sandbox/utils.d.ts.map +1 -1
- package/dist/sandbox/utils.js +0 -72
- package/dist/sandbox/utils.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vault/index.js +1 -1
- package/dist/vault/index.js.map +1 -1
- package/package.json +3 -1
- package/dist/harness/execution-env.d.ts +0 -4
- package/dist/harness/execution-env.d.ts.map +0 -1
- package/dist/harness/execution-env.js +0 -322
- package/dist/harness/execution-env.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,16 +11,44 @@ change in a minor release.
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
-
## [1.
|
|
15
|
-
|
|
16
|
-
###
|
|
17
|
-
|
|
18
|
-
- `
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
## [1.4.0-beta.0]
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- `web_fetch` reads a public web page or API from the host so the agent can check versions,
|
|
19
|
+
releases, and dates against the official source instead of answering from memory. HTML is
|
|
20
|
+
returned as Markdown without navigation; long results are bounded like MCP results, with the
|
|
21
|
+
full text saved in the workspace. It needs no key and refuses private, loopback, and link-local
|
|
22
|
+
addresses, including through redirects. It does not go through an HTTP proxy configured for
|
|
23
|
+
the daemon.
|
|
24
|
+
- The OCI sandbox runs on nerdctl, Podman, or Docker. mikan uses the first engine whose `info`
|
|
25
|
+
succeeds, in that order, and logs its choice; set `CONTAINER_ENGINE` to pin one.
|
|
26
|
+
- `--sandbox=oci` without an image uses `ghcr.io/geminixiang/mikan-sandbox:<mikan version>`, so
|
|
27
|
+
the sandbox image upgrades and rolls back with mikan. At startup mikan pulls the configured
|
|
28
|
+
image in the background; a failed pull logs a warning and offices keep the local image. A
|
|
29
|
+
stopped office's container is replaced when its image changed, and the old image is removed
|
|
30
|
+
once no container uses it. A running container is never replaced.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- `--sandbox=image:<image>` is now `--sandbox=oci:<image>`. `image:` still works, prints a
|
|
35
|
+
deprecation warning, and will be removed in the next major release; existing containers and
|
|
36
|
+
vaults are kept. `mikan onboard` and the PM2 template write `--sandbox=oci`.
|
|
37
|
+
- Container tools run through Pi's `pi-env` daemon over one connection per container, instead of
|
|
38
|
+
one engine `exec` per operation: file operations and commands take about 1 ms instead of
|
|
39
|
+
40–180 ms, and commands end as soon as mikan disconnects. String commands run with `bash` when
|
|
40
|
+
the container has it, and oversized command output is saved in the container's temporary
|
|
41
|
+
directory.
|
|
42
|
+
- Each container gets the mikan host's time zone. The sandbox image no longer sets
|
|
43
|
+
`TZ=Asia/Taipei` and is about 390 MB smaller: `vim`, `nano`, `less`, `bash-completion`,
|
|
44
|
+
`python3-venv`, `gnupg`, and gcloud's precompiled Python files are gone.
|
|
45
|
+
- Existing containers are recreated once on first use after upgrading, because mikan now
|
|
46
|
+
identifies a container's network and image by labels it writes at creation. Workspace and vault
|
|
47
|
+
mounts are kept; packages installed inside the container are not. On a host running more than
|
|
48
|
+
one engine, mikan may pick a different engine and create new containers there; set
|
|
49
|
+
`CONTAINER_ENGINE=docker` to keep Docker.
|
|
50
|
+
|
|
51
|
+
## [1.3.0] - 2026-10-10
|
|
24
52
|
|
|
25
53
|
### Added
|
|
26
54
|
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Self-hosted AI coding agent that works in your team's chat.
|
|
|
11
11
|
|
|
12
12
|
- **One office per conversation** — every Slack channel and DM gets its own workspace, sandbox, credentials, and memory; each thread runs as its own session.
|
|
13
13
|
- **Where your team already talks** — Slack, Telegram, Discord, and GitHub (experimental).
|
|
14
|
-
- **Real tools, contained** — in `
|
|
14
|
+
- **Real tools, contained** — in `oci:` mode, commands run in a per-office container with credentials from that office's vault.
|
|
15
15
|
|
|
16
16
|
Upgrading from 0.5.3? Follow [Upgrading from 0.5.3](src/content/docs/deployment.mdx#upgrading-from-053); `mikan migrate` converts the state in place.
|
|
17
17
|
|
|
@@ -27,7 +27,7 @@ mikan keeps the chat record, agent session, and execution runtime separate:
|
|
|
27
27
|
- **Chat / conversation data** is the platform-facing record: `log.jsonl`, attachments, and conversation files.
|
|
28
28
|
- **Session orchestration** turns platform events into agent runs, handles top-level/thread scopes, and persists agent sessions in one host-only SQLite `sessions.db` per office under the State dir.
|
|
29
29
|
- **mikan agent harness** (`src/harness/`, built on pi-durable and pi-ai) owns run preparation, authorized prompts/tools, response presentation, and native Pi session integration. Pi handles the model/tool loop, persistence, compaction, retries, and cancellation.
|
|
30
|
-
- **Sandbox runtime** is where tool commands execute: host, or a
|
|
30
|
+
- **Sandbox runtime** is where tool commands execute: host, an existing container, or a container mikan manages per office.
|
|
31
31
|
- **Vault** provides runtime credentials as env vars and mounted secret files.
|
|
32
32
|
|
|
33
33
|
## Features
|
|
@@ -35,7 +35,7 @@ mikan keeps the chat record, agent session, and execution runtime separate:
|
|
|
35
35
|
- **Multi-platform** — Slack, Telegram, Discord, and GitHub (experimental) adapters
|
|
36
36
|
- **Concurrent conversations** — Slack threads, Discord replies/threads, and Telegram reply chains run as independent sessions
|
|
37
37
|
- **Conversation offices** — one office directory and one sandbox runtime per conversation, with public/private visibility derived from the platform conversation type
|
|
38
|
-
- **Sandbox execution** — one managed
|
|
38
|
+
- **Sandbox execution** — one managed container per conversation office, on nerdctl, Podman, or Docker
|
|
39
39
|
- **Credential vaults** — `/login` stores credentials under `~/.mikan` and injects env into sandbox runs
|
|
40
40
|
- **Web session viewer** — read-only web view of the current session via `session` / `/session`
|
|
41
41
|
- **Persistent memory** — workspace-level and per-office `MEMORY.md`
|
|
@@ -45,20 +45,19 @@ mikan keeps the chat record, agent session, and execution runtime separate:
|
|
|
45
45
|
|
|
46
46
|
## Quick Start
|
|
47
47
|
|
|
48
|
-
You need Node.js 24.15 or later and Docker.
|
|
48
|
+
You need Node.js 24.15 or later and a container engine: nerdctl, Podman, or Docker.
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
51
|
npm i -g @geminixiang/mikan pm2
|
|
52
52
|
mikan onboard # pick a chat platform, a model, and a sandbox image
|
|
53
|
-
docker pull ghcr.io/geminixiang/mikan-sandbox:latest
|
|
54
53
|
pm2 start ~/.mikan/ecosystem.config.cjs && pm2 save
|
|
55
54
|
```
|
|
56
55
|
|
|
57
|
-
That's it: `@mention` the bot in a channel or send it a DM.
|
|
56
|
+
That's it: `@mention` the bot in a channel or send it a DM. mikan pulls the sandbox image when it starts, and pulls the matching image again after each upgrade; the recommended image is about 900 MB, so the first start takes a few minutes. Run `pm2 startup` once if mikan should start on boot.
|
|
58
57
|
|
|
59
58
|
Everything lives in `~/.mikan`: `settings.json` for behavior (model, sandbox limits, reply modes), `mikan.env` for secrets and platform tokens, and `ecosystem.config.cjs` for process supervision. To add another platform later, put its tokens in `mikan.env` and run `pm2 reload ~/.mikan/ecosystem.config.cjs` so pm2 rereads the file; `mikan env` lists every variable mikan reads.
|
|
60
59
|
|
|
61
|
-
Upgrade with `npm i -g @geminixiang/mikan`, check `mikan migrate --sandbox=
|
|
60
|
+
Upgrade with `npm i -g @geminixiang/mikan`, check `mikan migrate --sandbox=oci:<image> --dry-run` for pending State migrations, then `pm2 reload mikan`. See [the deployment guide](src/content/docs/deployment.mdx) for graceful shutdown and the health endpoint.
|
|
62
61
|
|
|
63
62
|
## Platforms
|
|
64
63
|
|
|
@@ -71,7 +70,7 @@ Slack threads, Discord replies/threads, and Telegram reply chains are mapped to
|
|
|
71
70
|
|
|
72
71
|
## Sandbox
|
|
73
72
|
|
|
74
|
-
Use `
|
|
73
|
+
Use `oci:<image>`: mikan starts one container and one credential vault per conversation office, and enforces office visibility. `mikan onboard` sets it up.
|
|
75
74
|
|
|
76
75
|
`host` (run directly on the machine) and `container:<name>` (one shared, existing container) still work but are not recommended: they give the agent the whole machine or one shared filesystem, skip `/login` credential injection, and do not enforce office visibility.
|
|
77
76
|
|
|
@@ -39,7 +39,7 @@ function ensureLoginVault(context) {
|
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
async function refreshCopiedVaultRuntime(context) {
|
|
42
|
-
if (context.services.sandbox.type !== "
|
|
42
|
+
if (context.services.sandbox.type !== "oci")
|
|
43
43
|
return undefined;
|
|
44
44
|
const targetAddress = createOfficeAddress(context.address.platform, context.vaultConversationId ?? context.conversationId);
|
|
45
45
|
const cleared = context.services.runtime?.refreshConversation(targetAddress);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../../src/adapters/commands/login.ts"],"names":[],"mappings":"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","sourcesContent":["import * as log from \"../../log.js\";\nimport { credentialAuthorizationKey, runtimeResourceKey } from \"../../sandbox/identity.js\";\nimport { sharedVaultKey } from \"../../vault/index.js\";\nimport { slashForms, matchCommand } from \"./manifest.js\";\nimport type { CommandContext, CommandHandler, ParsedLoginCommand } from \"./types.js\";\nimport { portalNotConfiguredLines, replySummary } from \"./utils.js\";\nimport { createOfficeAddress } from \"../../office/index.js\";\nimport { errorMessage } from \"../../unknown-values.js\";\n\nconst LOGIN_COMMANDS = slashForms(\"login\");\n\nconst SHARED_OPERATIONS = [\"create\", \"update\", \"delete\"] as const;\n\nexport function parseLoginCommand(text: string): ParsedLoginCommand | null {\n const matched = matchCommand(text, LOGIN_COMMANDS);\n if (!matched) return null;\n\n const [subcommand, operation, name, ...extra] = matched.args;\n if (!subcommand) return { action: \"setup\" };\n if (extra.length > 0) return null;\n\n const verb = subcommand.toLowerCase();\n if (verb === \"shared\") return parseSharedLogin(operation?.toLowerCase(), name);\n if (verb === \"copy\" && operation && !name) return { action: \"copy_shared\", name: operation };\n return operation ? null : { action: \"setup\" };\n}\n\nfunction parseSharedLogin(\n operation: string | undefined,\n name: string | undefined,\n): ParsedLoginCommand | null {\n if (operation === \"list\") return name ? null : { action: \"shared_list\" };\n const matched = SHARED_OPERATIONS.find((candidate) => candidate === operation);\n if (!matched || !name) return null;\n return { action: `shared_${matched}`, name };\n}\n\nfunction ensureLoginVault(context: CommandContext): string {\n const { services, platformUserId, conversationId, vaultConversationId } = context;\n return credentialAuthorizationKey(services.sandbox, {\n userId: platformUserId,\n address: createOfficeAddress(context.address.platform, vaultConversationId ?? conversationId),\n });\n}\n\nasync function refreshCopiedVaultRuntime(context: CommandContext): Promise<string | undefined> {\n if (context.services.sandbox.type !== \"image\") return undefined;\n\n const targetAddress = createOfficeAddress(\n context.address.platform,\n context.vaultConversationId ?? context.conversationId,\n );\n const cleared = context.services.runtime?.refreshConversation(targetAddress);\n if (cleared === false) {\n return \"A session is currently running, so the sandbox was not restarted. The copied credentials will be applied after the run finishes and the sandbox is recreated.\";\n }\n\n if (!context.services.provisioner) {\n return \"The cached session was refreshed. The sandbox will pick up copied credentials on the next provision.\";\n }\n\n const resourceKey = runtimeResourceKey(context.services.sandbox, {\n userId: context.platformUserId,\n address: targetAddress,\n });\n await context.services.provisioner.remove(resourceKey);\n return \"The sandbox container was removed and will be recreated with the copied env and file mounts on the next message.\";\n}\n\nexport class LoginCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const parsed = parseLoginCommand(context.commandText);\n if (!parsed) return false;\n\n if (!context.privateConversation) {\n await replySummary(context, \"Vault\", [\n \"為了保護你的憑證,`/login` 只能在與機器人的私訊中使用。\",\n \"請先私訊機器人,再重新執行 `/login`。\",\n ]);\n return true;\n }\n\n if (parsed.action === \"shared_list\") await listSharedProfiles(context);\n else if (parsed.action === \"shared_delete\") await deleteSharedProfile(context, parsed.name);\n else if (parsed.action === \"copy_shared\") await copySharedProfile(context, parsed.name);\n else await startLoginSetup(context, parsed);\n return true;\n }\n}\n\nasync function replyVaultError(context: CommandContext, error: unknown): Promise<void> {\n await replySummary(context, \"Vault\", [errorMessage(error)]);\n}\n\nasync function listSharedProfiles(context: CommandContext): Promise<void> {\n const profiles = context.services.vaultManager.listSharedVaults();\n await replySummary(\n context,\n \"Vault\",\n profiles.length > 0\n ? [\"Shared login profiles:\", ...profiles.map((name) => `- ${name}`)]\n : [\"No shared login profiles found.\"],\n );\n}\n\nasync function deleteSharedProfile(context: CommandContext, name: string): Promise<void> {\n try {\n const deleted = context.services.vaultManager.deleteSharedVault(name);\n await replySummary(context, \"Vault\", [\n deleted\n ? `Deleted shared login profile \\`${name}\\`.`\n : `Shared login profile \\`${name}\\` does not exist.`,\n ]);\n } catch (error) {\n await replyVaultError(context, error);\n }\n}\n\nasync function copySharedProfile(context: CommandContext, name: string): Promise<void> {\n try {\n const vaultId = ensureLoginVault(context);\n const result = context.services.vaultManager.copySharedVaultTo(name, vaultId);\n const refreshNote = await refreshCopiedVaultRuntime(context);\n await replySummary(context, \"Vault\", [\n `Copied shared login profile \\`${name}\\` into this conversation.`,\n \"Shared values overwrite matching conversation values; conversation-only values are kept.\",\n `Copied: ${result.envKeysCopied} env key(s), ${result.filesCopied} file(s).`,\n ...(refreshNote ? [refreshNote] : []),\n ]);\n } catch (error) {\n await replyVaultError(context, error);\n }\n}\n\nasync function startLoginSetup(context: CommandContext, parsed: ParsedLoginCommand): Promise<void> {\n if (!context.services.portalBaseUrl) {\n await replySummary(context, \"Vault\", portalNotConfiguredLines(\"Login\"));\n return;\n }\n\n let vaultId: string;\n try {\n vaultId = resolveLoginVaultId(context, parsed);\n } catch (error) {\n log.logWarning(\n `[${context.conversationId}] Failed to prepare login vault for ${context.platform}/${context.platformUserId}`,\n errorMessage(error),\n );\n await replySummary(context, \"Vault\", [\n \"Login setup failed on the server.\",\n \"請稍後重試,或聯絡管理員檢查 vault 儲存權限。\",\n ]);\n return;\n }\n\n const token = context.services.linkTokenStore.create(\n context.platform,\n context.platformUserId,\n context.conversationId,\n vaultId,\n \"\",\n );\n await replySummary(context, \"Vault\", [\n `${context.services.portalBaseUrl}/link?token=${token.token}`,\n `Target: ${loginVaultLabel(context, parsed, vaultId)} · Expires: 15 minutes`,\n ]);\n}\n\nfunction resolveLoginVaultId(context: CommandContext, parsed: ParsedLoginCommand): string {\n const isSharedSetup = parsed.action === \"shared_create\" || parsed.action === \"shared_update\";\n const vaultId = isSharedSetup ? (sharedVaultKey(parsed.name) ?? \"\") : ensureLoginVault(context);\n if (vaultId) return vaultId;\n throw new Error(\n isSharedSetup ? `Invalid shared login profile name: ${parsed.name}` : \"Invalid vault id\",\n );\n}\n\nfunction loginVaultLabel(\n context: CommandContext,\n parsed: ParsedLoginCommand,\n vaultId: string,\n): string {\n if (parsed.action === \"shared_create\" || parsed.action === \"shared_update\") {\n return `shared login profile (${parsed.name})`;\n }\n return context.services.sandbox.type === \"container\"\n ? `container vault (${vaultId})`\n : \"your vault\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../../src/adapters/commands/login.ts"],"names":[],"mappings":"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","sourcesContent":["import * as log from \"../../log.js\";\nimport { credentialAuthorizationKey, runtimeResourceKey } from \"../../sandbox/identity.js\";\nimport { sharedVaultKey } from \"../../vault/index.js\";\nimport { slashForms, matchCommand } from \"./manifest.js\";\nimport type { CommandContext, CommandHandler, ParsedLoginCommand } from \"./types.js\";\nimport { portalNotConfiguredLines, replySummary } from \"./utils.js\";\nimport { createOfficeAddress } from \"../../office/index.js\";\nimport { errorMessage } from \"../../unknown-values.js\";\n\nconst LOGIN_COMMANDS = slashForms(\"login\");\n\nconst SHARED_OPERATIONS = [\"create\", \"update\", \"delete\"] as const;\n\nexport function parseLoginCommand(text: string): ParsedLoginCommand | null {\n const matched = matchCommand(text, LOGIN_COMMANDS);\n if (!matched) return null;\n\n const [subcommand, operation, name, ...extra] = matched.args;\n if (!subcommand) return { action: \"setup\" };\n if (extra.length > 0) return null;\n\n const verb = subcommand.toLowerCase();\n if (verb === \"shared\") return parseSharedLogin(operation?.toLowerCase(), name);\n if (verb === \"copy\" && operation && !name) return { action: \"copy_shared\", name: operation };\n return operation ? null : { action: \"setup\" };\n}\n\nfunction parseSharedLogin(\n operation: string | undefined,\n name: string | undefined,\n): ParsedLoginCommand | null {\n if (operation === \"list\") return name ? null : { action: \"shared_list\" };\n const matched = SHARED_OPERATIONS.find((candidate) => candidate === operation);\n if (!matched || !name) return null;\n return { action: `shared_${matched}`, name };\n}\n\nfunction ensureLoginVault(context: CommandContext): string {\n const { services, platformUserId, conversationId, vaultConversationId } = context;\n return credentialAuthorizationKey(services.sandbox, {\n userId: platformUserId,\n address: createOfficeAddress(context.address.platform, vaultConversationId ?? conversationId),\n });\n}\n\nasync function refreshCopiedVaultRuntime(context: CommandContext): Promise<string | undefined> {\n if (context.services.sandbox.type !== \"oci\") return undefined;\n\n const targetAddress = createOfficeAddress(\n context.address.platform,\n context.vaultConversationId ?? context.conversationId,\n );\n const cleared = context.services.runtime?.refreshConversation(targetAddress);\n if (cleared === false) {\n return \"A session is currently running, so the sandbox was not restarted. The copied credentials will be applied after the run finishes and the sandbox is recreated.\";\n }\n\n if (!context.services.provisioner) {\n return \"The cached session was refreshed. The sandbox will pick up copied credentials on the next provision.\";\n }\n\n const resourceKey = runtimeResourceKey(context.services.sandbox, {\n userId: context.platformUserId,\n address: targetAddress,\n });\n await context.services.provisioner.remove(resourceKey);\n return \"The sandbox container was removed and will be recreated with the copied env and file mounts on the next message.\";\n}\n\nexport class LoginCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const parsed = parseLoginCommand(context.commandText);\n if (!parsed) return false;\n\n if (!context.privateConversation) {\n await replySummary(context, \"Vault\", [\n \"為了保護你的憑證,`/login` 只能在與機器人的私訊中使用。\",\n \"請先私訊機器人,再重新執行 `/login`。\",\n ]);\n return true;\n }\n\n if (parsed.action === \"shared_list\") await listSharedProfiles(context);\n else if (parsed.action === \"shared_delete\") await deleteSharedProfile(context, parsed.name);\n else if (parsed.action === \"copy_shared\") await copySharedProfile(context, parsed.name);\n else await startLoginSetup(context, parsed);\n return true;\n }\n}\n\nasync function replyVaultError(context: CommandContext, error: unknown): Promise<void> {\n await replySummary(context, \"Vault\", [errorMessage(error)]);\n}\n\nasync function listSharedProfiles(context: CommandContext): Promise<void> {\n const profiles = context.services.vaultManager.listSharedVaults();\n await replySummary(\n context,\n \"Vault\",\n profiles.length > 0\n ? [\"Shared login profiles:\", ...profiles.map((name) => `- ${name}`)]\n : [\"No shared login profiles found.\"],\n );\n}\n\nasync function deleteSharedProfile(context: CommandContext, name: string): Promise<void> {\n try {\n const deleted = context.services.vaultManager.deleteSharedVault(name);\n await replySummary(context, \"Vault\", [\n deleted\n ? `Deleted shared login profile \\`${name}\\`.`\n : `Shared login profile \\`${name}\\` does not exist.`,\n ]);\n } catch (error) {\n await replyVaultError(context, error);\n }\n}\n\nasync function copySharedProfile(context: CommandContext, name: string): Promise<void> {\n try {\n const vaultId = ensureLoginVault(context);\n const result = context.services.vaultManager.copySharedVaultTo(name, vaultId);\n const refreshNote = await refreshCopiedVaultRuntime(context);\n await replySummary(context, \"Vault\", [\n `Copied shared login profile \\`${name}\\` into this conversation.`,\n \"Shared values overwrite matching conversation values; conversation-only values are kept.\",\n `Copied: ${result.envKeysCopied} env key(s), ${result.filesCopied} file(s).`,\n ...(refreshNote ? [refreshNote] : []),\n ]);\n } catch (error) {\n await replyVaultError(context, error);\n }\n}\n\nasync function startLoginSetup(context: CommandContext, parsed: ParsedLoginCommand): Promise<void> {\n if (!context.services.portalBaseUrl) {\n await replySummary(context, \"Vault\", portalNotConfiguredLines(\"Login\"));\n return;\n }\n\n let vaultId: string;\n try {\n vaultId = resolveLoginVaultId(context, parsed);\n } catch (error) {\n log.logWarning(\n `[${context.conversationId}] Failed to prepare login vault for ${context.platform}/${context.platformUserId}`,\n errorMessage(error),\n );\n await replySummary(context, \"Vault\", [\n \"Login setup failed on the server.\",\n \"請稍後重試,或聯絡管理員檢查 vault 儲存權限。\",\n ]);\n return;\n }\n\n const token = context.services.linkTokenStore.create(\n context.platform,\n context.platformUserId,\n context.conversationId,\n vaultId,\n \"\",\n );\n await replySummary(context, \"Vault\", [\n `${context.services.portalBaseUrl}/link?token=${token.token}`,\n `Target: ${loginVaultLabel(context, parsed, vaultId)} · Expires: 15 minutes`,\n ]);\n}\n\nfunction resolveLoginVaultId(context: CommandContext, parsed: ParsedLoginCommand): string {\n const isSharedSetup = parsed.action === \"shared_create\" || parsed.action === \"shared_update\";\n const vaultId = isSharedSetup ? (sharedVaultKey(parsed.name) ?? \"\") : ensureLoginVault(context);\n if (vaultId) return vaultId;\n throw new Error(\n isSharedSetup ? `Invalid shared login profile name: ${parsed.name}` : \"Invalid vault id\",\n );\n}\n\nfunction loginVaultLabel(\n context: CommandContext,\n parsed: ParsedLoginCommand,\n vaultId: string,\n): string {\n if (parsed.action === \"shared_create\" || parsed.action === \"shared_update\") {\n return `shared login profile (${parsed.name})`;\n }\n return context.services.sandbox.type === \"container\"\n ? `container vault (${vaultId})`\n : \"your vault\";\n}\n"]}
|
|
@@ -25,9 +25,9 @@ export class SandboxCommandHandler {
|
|
|
25
25
|
const parsed = parseSandboxCommand(context.commandText);
|
|
26
26
|
if (!parsed)
|
|
27
27
|
return false;
|
|
28
|
-
if (context.services.sandbox.type !== "
|
|
28
|
+
if (context.services.sandbox.type !== "oci" || !context.services.resourceController) {
|
|
29
29
|
await replySummary(context, "Sandbox", [
|
|
30
|
-
"`/pi-sandbox` 目前只支援 `
|
|
30
|
+
"`/pi-sandbox` 目前只支援 `oci:*` managed sandbox。",
|
|
31
31
|
]);
|
|
32
32
|
return true;
|
|
33
33
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../../src/adapters/commands/sandbox.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolveWorkspaceProjection } from \"../../office/projection.js\";\nimport { runtimeResourceKey } from \"../../sandbox/identity.js\";\nimport { applyOfficeVisibility } from \"../../settings/apply.js\";\nimport { slashForms, matchCommand } from \"./manifest.js\";\nimport type { CommandContext, CommandHandler, ParsedSandboxCommand } from \"./types.js\";\nimport { replySummary } from \"./utils.js\";\n\nconst SANDBOX_COMMANDS = slashForms(\"sandbox\");\n\nexport function parseSandboxCommand(text: string): ParsedSandboxCommand | null {\n const matched = matchCommand(text, SANDBOX_COMMANDS, { stripMention: true });\n if (!matched) return null;\n\n const action = matched.args[0]?.toLowerCase();\n if (action === \"boost\" && matched.args.length === 1) {\n return { action };\n }\n if (action === \"visibility\" && matched.args.length <= 2) {\n return {\n action,\n visibility: matched.args[1]?.toLowerCase(),\n };\n }\n return {};\n}\n\nexport class SandboxCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const parsed = parseSandboxCommand(context.commandText);\n if (!parsed) return false;\n\n if (context.services.sandbox.type !== \"image\" || !context.services.resourceController) {\n await replySummary(context, \"Sandbox\", [\n \"`/pi-sandbox` 目前只支援 `image:*` managed sandbox。\",\n ]);\n return true;\n }\n\n const controller = context.services.resourceController;\n const containerKey = runtimeResourceKey(context.services.sandbox, {\n userId: context.platformUserId,\n address: context.address,\n });\n if (parsed.action === \"boost\") {\n await handleBoost(context, controller, containerKey);\n } else if (parsed.action === \"visibility\") {\n await handleVisibility(context, parsed);\n } else {\n await showSandboxStatus(context, controller, containerKey);\n }\n return true;\n }\n}\n\nasync function handleBoost(\n context: CommandContext,\n controller: NonNullable<CommandContext[\"services\"][\"resourceController\"]>,\n containerKey: string,\n): Promise<void> {\n const boostLimits = controller.getBoostLimits();\n if (!boostLimits?.cpus && !boostLimits?.memory) {\n await replySummary(context, \"Sandbox Boost\", [\n \"此 mikan instance 尚未設定 sandbox boost 規格。\",\n \"請先在全域 settings.json 設定 `sandbox.boost`。\",\n ]);\n return;\n }\n\n const status = await controller.boost(containerKey);\n await replySummary(context, \"Sandbox Boost\", [\n \"已暫時提升此 conversation 的 sandbox 規格。\",\n `Current: ${formatLimits(status.limits)}`,\n \"boost 會在此 sandbox runtime 關閉後結束。\",\n ]);\n}\n\nfunction describeVisibility(projection: ReturnType<typeof resolveWorkspaceProjection>): string {\n const source =\n projection.source === \"override\"\n ? \"admin 覆寫\"\n : projection.source === \"platform\"\n ? \"依平台頻道類型\"\n : \"Slack 尚未回報頻道類型,預設 private\";\n return `${projection.visibility}(${source})`;\n}\n\nasync function handleVisibility(\n context: CommandContext,\n parsed: ParsedSandboxCommand,\n): Promise<void> {\n const office = context.services.workspace.office(context.address);\n const projection = resolveWorkspaceProjection(office);\n if (parsed.visibility === undefined) {\n await replySummary(context, \"Office Visibility\", [\n `Current: ${describeVisibility(projection)}`,\n \"\",\n \"public:其他 office 可唯讀這個 office,且可寫入共用 MEMORY.md / skills。\",\n \"private:只有自己看得到,共用 MEMORY.md / skills 唯讀。\",\n \"預設跟隨 Slack 頻道類型;private 頻道與 DM 一律 private。\",\n \"admin 覆寫:`/pi-sandbox visibility <private|default>`(只能把 public 頻道改為 private)\",\n ]);\n return;\n }\n if (parsed.visibility !== \"private\" && parsed.visibility !== \"default\") {\n await replySummary(context, \"Office Visibility\", [\n `未知的值:\\`${parsed.visibility}\\`。可用值:\\`private\\`、\\`default\\``,\n ]);\n return;\n }\n const result = applyOfficeVisibility(\n context.services.runtime,\n office,\n parsed.visibility === \"private\" ? \"private\" : null,\n );\n if (!result.ok) {\n await replySummary(context, \"Office Visibility\", [\n \"目前有工作正在執行,無法切換 visibility。等執行結束後再試一次。\",\n ]);\n return;\n }\n const updated = resolveWorkspaceProjection(office);\n await replySummary(context, \"Office Visibility\", [\n `Visibility 已更新。Current: ${describeVisibility(updated)}`,\n \"下一則訊息時會以新的掛載重建 sandbox 容器;容器內容會保留。\",\n ]);\n}\n\nasync function showSandboxStatus(\n context: CommandContext,\n controller: NonNullable<CommandContext[\"services\"][\"resourceController\"]>,\n containerKey: string,\n): Promise<void> {\n const status = controller.getLimitStatus(containerKey);\n const defaultLimits = controller.getDefaultLimits();\n const boostLimits = controller.getBoostLimits();\n const projection = resolveWorkspaceProjection(context.services.workspace.office(context.address));\n await replySummary(\n context,\n \"Sandbox\",\n [\n `Current: ${formatLimits(status.limits)}`,\n `Status: ${status.boosted ? \"boosted\" : \"default\"}`,\n `Office visibility: ${describeVisibility(projection)}`,\n \"\",\n `Default: ${formatLimits(defaultLimits)}`,\n boostLimits ? `Boost: ${formatLimits({ ...defaultLimits, ...boostLimits })}` : undefined,\n ].filter((line): line is string => line !== undefined),\n );\n}\n\nfunction formatLimits(limits: { cpus?: string; memory?: string } | undefined): string {\n return `CPU ${limits?.cpus ?? \"unlimited\"} / Memory ${limits?.memory ?? \"unlimited\"}`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../../src/adapters/commands/sandbox.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolveWorkspaceProjection } from \"../../office/projection.js\";\nimport { runtimeResourceKey } from \"../../sandbox/identity.js\";\nimport { applyOfficeVisibility } from \"../../settings/apply.js\";\nimport { slashForms, matchCommand } from \"./manifest.js\";\nimport type { CommandContext, CommandHandler, ParsedSandboxCommand } from \"./types.js\";\nimport { replySummary } from \"./utils.js\";\n\nconst SANDBOX_COMMANDS = slashForms(\"sandbox\");\n\nexport function parseSandboxCommand(text: string): ParsedSandboxCommand | null {\n const matched = matchCommand(text, SANDBOX_COMMANDS, { stripMention: true });\n if (!matched) return null;\n\n const action = matched.args[0]?.toLowerCase();\n if (action === \"boost\" && matched.args.length === 1) {\n return { action };\n }\n if (action === \"visibility\" && matched.args.length <= 2) {\n return {\n action,\n visibility: matched.args[1]?.toLowerCase(),\n };\n }\n return {};\n}\n\nexport class SandboxCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const parsed = parseSandboxCommand(context.commandText);\n if (!parsed) return false;\n\n if (context.services.sandbox.type !== \"oci\" || !context.services.resourceController) {\n await replySummary(context, \"Sandbox\", [\n \"`/pi-sandbox` 目前只支援 `oci:*` managed sandbox。\",\n ]);\n return true;\n }\n\n const controller = context.services.resourceController;\n const containerKey = runtimeResourceKey(context.services.sandbox, {\n userId: context.platformUserId,\n address: context.address,\n });\n if (parsed.action === \"boost\") {\n await handleBoost(context, controller, containerKey);\n } else if (parsed.action === \"visibility\") {\n await handleVisibility(context, parsed);\n } else {\n await showSandboxStatus(context, controller, containerKey);\n }\n return true;\n }\n}\n\nasync function handleBoost(\n context: CommandContext,\n controller: NonNullable<CommandContext[\"services\"][\"resourceController\"]>,\n containerKey: string,\n): Promise<void> {\n const boostLimits = controller.getBoostLimits();\n if (!boostLimits?.cpus && !boostLimits?.memory) {\n await replySummary(context, \"Sandbox Boost\", [\n \"此 mikan instance 尚未設定 sandbox boost 規格。\",\n \"請先在全域 settings.json 設定 `sandbox.boost`。\",\n ]);\n return;\n }\n\n const status = await controller.boost(containerKey);\n await replySummary(context, \"Sandbox Boost\", [\n \"已暫時提升此 conversation 的 sandbox 規格。\",\n `Current: ${formatLimits(status.limits)}`,\n \"boost 會在此 sandbox runtime 關閉後結束。\",\n ]);\n}\n\nfunction describeVisibility(projection: ReturnType<typeof resolveWorkspaceProjection>): string {\n const source =\n projection.source === \"override\"\n ? \"admin 覆寫\"\n : projection.source === \"platform\"\n ? \"依平台頻道類型\"\n : \"Slack 尚未回報頻道類型,預設 private\";\n return `${projection.visibility}(${source})`;\n}\n\nasync function handleVisibility(\n context: CommandContext,\n parsed: ParsedSandboxCommand,\n): Promise<void> {\n const office = context.services.workspace.office(context.address);\n const projection = resolveWorkspaceProjection(office);\n if (parsed.visibility === undefined) {\n await replySummary(context, \"Office Visibility\", [\n `Current: ${describeVisibility(projection)}`,\n \"\",\n \"public:其他 office 可唯讀這個 office,且可寫入共用 MEMORY.md / skills。\",\n \"private:只有自己看得到,共用 MEMORY.md / skills 唯讀。\",\n \"預設跟隨 Slack 頻道類型;private 頻道與 DM 一律 private。\",\n \"admin 覆寫:`/pi-sandbox visibility <private|default>`(只能把 public 頻道改為 private)\",\n ]);\n return;\n }\n if (parsed.visibility !== \"private\" && parsed.visibility !== \"default\") {\n await replySummary(context, \"Office Visibility\", [\n `未知的值:\\`${parsed.visibility}\\`。可用值:\\`private\\`、\\`default\\``,\n ]);\n return;\n }\n const result = applyOfficeVisibility(\n context.services.runtime,\n office,\n parsed.visibility === \"private\" ? \"private\" : null,\n );\n if (!result.ok) {\n await replySummary(context, \"Office Visibility\", [\n \"目前有工作正在執行,無法切換 visibility。等執行結束後再試一次。\",\n ]);\n return;\n }\n const updated = resolveWorkspaceProjection(office);\n await replySummary(context, \"Office Visibility\", [\n `Visibility 已更新。Current: ${describeVisibility(updated)}`,\n \"下一則訊息時會以新的掛載重建 sandbox 容器;容器內容會保留。\",\n ]);\n}\n\nasync function showSandboxStatus(\n context: CommandContext,\n controller: NonNullable<CommandContext[\"services\"][\"resourceController\"]>,\n containerKey: string,\n): Promise<void> {\n const status = controller.getLimitStatus(containerKey);\n const defaultLimits = controller.getDefaultLimits();\n const boostLimits = controller.getBoostLimits();\n const projection = resolveWorkspaceProjection(context.services.workspace.office(context.address));\n await replySummary(\n context,\n \"Sandbox\",\n [\n `Current: ${formatLimits(status.limits)}`,\n `Status: ${status.boosted ? \"boosted\" : \"default\"}`,\n `Office visibility: ${describeVisibility(projection)}`,\n \"\",\n `Default: ${formatLimits(defaultLimits)}`,\n boostLimits ? `Boost: ${formatLimits({ ...defaultLimits, ...boostLimits })}` : undefined,\n ].filter((line): line is string => line !== undefined),\n );\n}\n\nfunction formatLimits(limits: { cpus?: string; memory?: string } | undefined): string {\n return `CPU ${limits?.cpus ?? \"unlimited\"} / Memory ${limits?.memory ?? \"unlimited\"}`;\n}\n"]}
|
package/dist/cli/boot.js
CHANGED
|
@@ -8,7 +8,7 @@ function bootCommand() {
|
|
|
8
8
|
.description("Multi-platform chat agent daemon")
|
|
9
9
|
.helpOption(false)
|
|
10
10
|
.argument("[working-directory...]", "Workspace directory (default: ~/.mikan/workspace)")
|
|
11
|
-
.option("--sandbox <spec>", "host | container:<name> |
|
|
11
|
+
.option("--sandbox <spec>", "host | container:<name> | oci | oci:<image[:tag]>", nonEmptyValue)
|
|
12
12
|
.option("--onboard", "Interactive first-run setup (also: mikan onboard)")
|
|
13
13
|
.option("-v, --version", "Print version")
|
|
14
14
|
.option("-V", "Print version")
|
package/dist/cli/boot.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boot.js","sourceRoot":"","sources":["../../src/cli/boot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE3E,MAAM,WAAW,GAAG,CAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAU,CAAC;AAS1D,SAAS,WAAW;IAClB,OAAO,UAAU,CAAC,OAAO,CAAC;SACvB,WAAW,CAAC,kCAAkC,CAAC;SAC/C,UAAU,CAAC,KAAK,CAAC;SACjB,QAAQ,CAAC,wBAAwB,EAAE,mDAAmD,CAAC;SACvF,MAAM,CAAC,kBAAkB,EAAE
|
|
1
|
+
{"version":3,"file":"boot.js","sourceRoot":"","sources":["../../src/cli/boot.ts"],"names":[],"mappings":"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","sourcesContent":["import { join, resolve } from \"node:path\";\nimport { envSummaryLines } from \"../env-manifest.js\";\nimport { parseSandboxArg } from \"../sandbox/registry.js\";\nimport type { BootPlan } from \"./types.js\";\nimport { cliCommand, nonEmptyValue, stateDirPath } from \"./arg-grammar.js\";\n\nconst SUBCOMMANDS = [\"migrate\", \"office\", \"env\"] as const;\n\ninterface BootOptions {\n sandbox?: string;\n help?: boolean;\n version?: boolean;\n onboard?: boolean;\n}\n\nfunction bootCommand() {\n return cliCommand(\"mikan\")\n .description(\"Multi-platform chat agent daemon\")\n .helpOption(false)\n .argument(\"[working-directory...]\", \"Workspace directory (default: ~/.mikan/workspace)\")\n .option(\"--sandbox <spec>\", \"host | container:<name> | oci | oci:<image[:tag]>\", nonEmptyValue)\n .option(\"--onboard\", \"Interactive first-run setup (also: mikan onboard)\")\n .option(\"-v, --version\", \"Print version\")\n .option(\"-V\", \"Print version\")\n .option(\"-h, --help\", \"Show help\");\n}\n\nexport function resolveBoot(args: string[] = process.argv.slice(2)): BootPlan {\n const routed = subcommandPlan(args);\n if (routed) return routed;\n\n const command = bootCommand();\n command.parse(args, { from: \"user\" });\n const options = command.opts<BootOptions & { V?: boolean }>();\n const onboardFirst = args[0] === \"onboard\";\n const workingDirArg = (onboardFirst ? command.args.slice(1) : command.args).at(-1);\n const sandboxArg = options.sandbox;\n const stateDir = stateDirPath();\n return {\n mode: bootMode({ ...options, version: options.version || options.V }, onboardFirst),\n stateDir,\n workingDir: workingDirArg ? resolve(workingDirArg) : join(stateDir, \"workspace\"),\n workingDirExplicit: workingDirArg !== undefined,\n sandbox: sandboxArg === undefined ? { type: \"host\" } : parseSandboxArg(sandboxArg),\n };\n}\n\nfunction subcommandPlan(args: string[]): BootPlan | undefined {\n const mode = SUBCOMMANDS.find((name) => name === args[0]);\n if (!mode) return undefined;\n const stateDir = stateDirPath();\n return {\n mode,\n migrateArgs: mode === \"migrate\" ? args.slice(1) : undefined,\n officeArgs: mode === \"office\" ? args.slice(1) : undefined,\n stateDir,\n workingDir: join(stateDir, \"workspace\"),\n workingDirExplicit: false,\n sandbox: { type: \"host\" },\n };\n}\n\nfunction bootMode(options: BootOptions, onboardFirst: boolean): BootPlan[\"mode\"] {\n if (options.help) return \"help\";\n if (options.version) return \"version\";\n if (onboardFirst || options.onboard) return \"onboard\";\n return \"run\";\n}\n\nexport function helpText(): string {\n return (\n bootCommand().helpInformation() +\n `\nCommands:\n mikan migrate --sandbox <spec> [--dry-run] Apply pending state migrations (stop the daemon first).\n mikan office list List office keys with their platform and conversation id.\n mikan env Show environment-variable inventory.\n mikan onboard Interactive first-run setup.\n\nEnvironment (platform tokens select which chat adapters start):\n${envSummaryLines().join(\"\\n\")}\nRun mikan env for the full inventory.`\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/cli/migrate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/cli/migrate.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AA6CxD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAsD3F"}
|
package/dist/cli/migrate.js
CHANGED
|
@@ -7,13 +7,14 @@ import { InvalidArgumentError } from "commander";
|
|
|
7
7
|
import { platformIsActive, readEnv } from "../env-manifest.js";
|
|
8
8
|
import { pendingMigrations, runMigrations } from "../migrations/index.js";
|
|
9
9
|
import { parseSandboxArg } from "../sandbox/registry.js";
|
|
10
|
+
import { containerEngine } from "../sandbox/engine.js";
|
|
10
11
|
import { globalSettingsPath } from "../settings/index.js";
|
|
11
12
|
import { assertPlatformName } from "../office/index.js";
|
|
12
13
|
import { errorMessage } from "../unknown-values.js";
|
|
13
14
|
import { cliCommand, commandExitCode, nonEmptyValue, stateDirPath } from "./arg-grammar.js";
|
|
14
15
|
const PLATFORMS = ["slack", "telegram", "discord", "github"];
|
|
15
16
|
const execFileAsync = promisify(execFile);
|
|
16
|
-
const dockerCli = async (args) => (await execFileAsync(
|
|
17
|
+
const dockerCli = async (args) => (await execFileAsync(containerEngine(), [...args])).stdout;
|
|
17
18
|
function collectOwner(value, owners) {
|
|
18
19
|
const separator = value.lastIndexOf("=");
|
|
19
20
|
if (separator <= 0)
|
|
@@ -39,7 +40,7 @@ export async function runMigrateCommand(argv, docker = dockerCli) {
|
|
|
39
40
|
const command = cliCommand("mikan migrate")
|
|
40
41
|
.description("Apply pending state migrations (stop the daemon first)")
|
|
41
42
|
.option("--workspace <dir>", "Workspace directory (default: ~/.mikan/workspace)")
|
|
42
|
-
.requiredOption("--sandbox <spec>", "The daemon's --sandbox value: host | container:<name> |
|
|
43
|
+
.requiredOption("--sandbox <spec>", "The daemon's --sandbox value: host | container:<name> | oci:<image>", nonEmptyValue)
|
|
43
44
|
.option("--dry-run", "List what each pending migration would change, without writing")
|
|
44
45
|
.option("--owner <conversationId=platform>", "Owning platform for a legacy conversation directory (repeatable)", collectOwner, new Map());
|
|
45
46
|
try {
|
package/dist/cli/migrate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/cli/migrate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE5F,MAAM,SAAS,GAA4B,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AAEtF,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,MAAM,SAAS,GAAc,KAAK,EAAE,IAAI,EAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/cli/migrate.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFile } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { InvalidArgumentError } from \"commander\";\nimport { platformIsActive, readEnv } from \"../env-manifest.js\";\nimport { pendingMigrations, runMigrations } from \"../migrations/index.js\";\nimport { parseSandboxArg } from \"../sandbox/registry.js\";\nimport { containerEngine } from \"../sandbox/engine.js\";\nimport { globalSettingsPath } from \"../settings/index.js\";\nimport type { DockerCli } from \"../migrations/types.js\";\nimport { assertPlatformName } from \"../office/index.js\";\nimport type { PlatformName } from \"../types.js\";\nimport { errorMessage } from \"../unknown-values.js\";\nimport { cliCommand, commandExitCode, nonEmptyValue, stateDirPath } from \"./arg-grammar.js\";\n\nconst PLATFORMS: readonly PlatformName[] = [\"slack\", \"telegram\", \"discord\", \"github\"];\n\nconst execFileAsync = promisify(execFile);\n\nconst dockerCli: DockerCli = async (args) =>\n (await execFileAsync(containerEngine(), [...args])).stdout;\n\nfunction collectOwner(value: string, owners: Map<string, PlatformName>): Map<string, PlatformName> {\n const separator = value.lastIndexOf(\"=\");\n if (separator <= 0) throw new InvalidArgumentError(\"expected <conversationId>=<platform>\");\n try {\n owners.set(value.slice(0, separator), assertPlatformName(value.slice(separator + 1)));\n } catch (error) {\n throw new InvalidArgumentError(errorMessage(error));\n }\n return owners;\n}\n\nfunction assertInstallPaths(stateDir: string, workspaceRoot: string): void {\n const settingsPath = globalSettingsPath(stateDir);\n if (!existsSync(settingsPath)) {\n throw new Error(\n `No settings at ${settingsPath}; move the state directory the daemon ran with to ${stateDir}`,\n );\n }\n if (!existsSync(workspaceRoot)) {\n throw new Error(\n `Workspace ${workspaceRoot} does not exist; pass the working directory the daemon ran with as --workspace`,\n );\n }\n}\n\ninterface MigrateOptions {\n workspace?: string;\n dryRun?: boolean;\n sandbox: string;\n owner: Map<string, PlatformName>;\n}\n\nexport async function runMigrateCommand(argv: string[], docker = dockerCli): Promise<number> {\n const command = cliCommand(\"mikan migrate\")\n .description(\"Apply pending state migrations (stop the daemon first)\")\n .option(\"--workspace <dir>\", \"Workspace directory (default: ~/.mikan/workspace)\")\n .requiredOption(\n \"--sandbox <spec>\",\n \"The daemon's --sandbox value: host | container:<name> | oci:<image>\",\n nonEmptyValue,\n )\n .option(\"--dry-run\", \"List what each pending migration would change, without writing\")\n .option(\n \"--owner <conversationId=platform>\",\n \"Owning platform for a legacy conversation directory (repeatable)\",\n collectOwner,\n new Map<string, PlatformName>(),\n );\n try {\n command.parse(argv, { from: \"user\" });\n } catch (error) {\n return commandExitCode(error, command);\n }\n const options = command.opts<MigrateOptions>();\n const stateDir = stateDirPath();\n const dryRun = options.dryRun ?? false;\n const workspaceRoot = options.workspace\n ? resolve(options.workspace)\n : join(stateDir, \"workspace\");\n try {\n const sandbox = parseSandboxArg(options.sandbox);\n if (pendingMigrations(stateDir).length > 0) assertInstallPaths(stateDir, workspaceRoot);\n const ran = await runMigrations({\n workspaceRoot,\n stateDir,\n dryRun,\n owners: options.owner,\n sandbox,\n enabledPlatforms: PLATFORMS.filter((platform) => platformIsActive(platform)),\n piAgentDir: readEnv(\"PI_CODING_AGENT_DIR\") ?? join(homedir(), \".pi\", \"agent\"),\n modelsPath: join(stateDir, \"models.json\"),\n docker,\n report: (line) => console.log(line),\n });\n if (ran.length === 0) console.log(\"No pending migrations.\");\n else if (!dryRun) console.log(`Applied ${ran.length}.`);\n else {\n console.log(`Dry run: ${ran.length} pending.`);\n console.log(\"Each step was previewed against the files as they are now, so a step may find\");\n console.log(\"more to do once the steps before it have run.\");\n }\n return 0;\n } catch (error) {\n console.error(`Migration failed: ${errorMessage(error)}`);\n return 1;\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboard.d.ts","sourceRoot":"","sources":["../../src/cli/onboard.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"onboard.d.ts","sourceRoot":"","sources":["../../src/cli/onboard.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAoI5C,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAUhG;AAUD,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,SAAS,EACb,KAAK,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B,OAAO,CAAC,MAAM,CAAC,CA2DjB;AAED,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBzE"}
|
package/dist/cli/onboard.js
CHANGED
|
@@ -5,7 +5,9 @@ import { ENV_MANIFEST, envSetReport, readEnv } from "../env-manifest.js";
|
|
|
5
5
|
import { createGlobalSettingsFile } from "../settings/index.js";
|
|
6
6
|
import { recordAllMigrations } from "../migrations/index.js";
|
|
7
7
|
import { atomicWritePrivateFile } from "../file-guards.js";
|
|
8
|
-
import { MINIMAL_SANDBOX_IMAGE, PM2_ECOSYSTEM_FILENAME,
|
|
8
|
+
import { MINIMAL_SANDBOX_IMAGE, PM2_ECOSYSTEM_FILENAME, renderPm2Ecosystem, } from "./pm2-ecosystem.js";
|
|
9
|
+
import { PUBLISHED_SANDBOX_SPEC } from "../sandbox/registry.js";
|
|
10
|
+
import { publishedSandboxImage } from "../sandbox/published-image.js";
|
|
9
11
|
class OnboardAborted extends Error {
|
|
10
12
|
constructor() {
|
|
11
13
|
super("Onboarding aborted.");
|
|
@@ -93,18 +95,18 @@ async function askLlm(io, env) {
|
|
|
93
95
|
}, null, 2);
|
|
94
96
|
return { llm: { provider, model: ids[0] }, modelsJson };
|
|
95
97
|
}
|
|
96
|
-
async function
|
|
97
|
-
io.print("\nStep 3/3 — sandbox image (one
|
|
98
|
+
async function askSandboxSpec(io) {
|
|
99
|
+
io.print("\nStep 3/3 — sandbox image (one container per conversation; needs nerdctl, Podman, or Docker)");
|
|
98
100
|
const index = await askChoice(io, [
|
|
99
|
-
`${
|
|
101
|
+
`${publishedSandboxImage()} (recommended: git, gh, gcloud, node, python, browser; follows mikan upgrades)`,
|
|
100
102
|
`${MINIMAL_SANDBOX_IMAGE} (minimal: shell only, no git or package installs)`,
|
|
101
103
|
"Another image from Docker Hub or a registry…",
|
|
102
104
|
], "Sandbox image");
|
|
103
105
|
if (index === 0)
|
|
104
|
-
return
|
|
106
|
+
return PUBLISHED_SANDBOX_SPEC;
|
|
105
107
|
if (index === 1)
|
|
106
|
-
return MINIMAL_SANDBOX_IMAGE
|
|
107
|
-
return askRequired(io, " Image (e.g. registry/name:tag): ")
|
|
108
|
+
return `oci:${MINIMAL_SANDBOX_IMAGE}`;
|
|
109
|
+
return `oci:${await askRequired(io, " Image (e.g. registry/name:tag): ")}`;
|
|
108
110
|
}
|
|
109
111
|
export function renderEnvFile(existing, vars) {
|
|
110
112
|
const pending = new Map(Object.entries(vars));
|
|
@@ -138,13 +140,13 @@ export async function runOnboardWizard(stateDir, io, paths) {
|
|
|
138
140
|
const env = {};
|
|
139
141
|
const { githubRepos } = await askAdapter(io, env);
|
|
140
142
|
const { llm, modelsJson } = await askLlm(io, env);
|
|
141
|
-
const
|
|
143
|
+
const sandboxSpec = await askSandboxSpec(io);
|
|
142
144
|
const modelsPath = join(stateDir, "models.json");
|
|
143
145
|
if (modelsJson && existsSync(modelsPath)) {
|
|
144
146
|
io.print(`models.json already exists at ${modelsPath}. Add the provider manually before retrying; nothing was written.`);
|
|
145
147
|
return 1;
|
|
146
148
|
}
|
|
147
|
-
io.print(`Review settings\nProvider: ${llm.provider}\nModel: ${llm.model}\nSandbox:
|
|
149
|
+
io.print(`Review settings\nProvider: ${llm.provider}\nModel: ${llm.model}\nSandbox: ${sandboxSpec}\nSettings: ${settingsPath}\nCredentials: ${paths?.envFilePath ?? join(stateDir, "mikan.env")}${modelsJson ? `\nModels: ${modelsPath}` : ""}\nSecrets are hidden.`);
|
|
148
150
|
if (!(await io.confirm("Save these settings?"))) {
|
|
149
151
|
io.print("Onboarding cancelled; nothing was written.");
|
|
150
152
|
return 1;
|
|
@@ -166,13 +168,13 @@ export async function runOnboardWizard(stateDir, io, paths) {
|
|
|
166
168
|
io.print(`Kept existing ${ecosystemPath}`);
|
|
167
169
|
}
|
|
168
170
|
else {
|
|
169
|
-
atomicWritePrivateFile(ecosystemPath, renderPm2Ecosystem(
|
|
171
|
+
atomicWritePrivateFile(ecosystemPath, renderPm2Ecosystem(sandboxSpec));
|
|
170
172
|
io.print(`Wrote ${ecosystemPath}`);
|
|
171
173
|
}
|
|
172
174
|
io.print("Next steps:");
|
|
173
|
-
io.print(`
|
|
174
|
-
io.print(`
|
|
175
|
-
io.print(
|
|
175
|
+
io.print(` pm2 start ${ecosystemPath} && pm2 save`);
|
|
176
|
+
io.print(` Foreground instead: set -a; source ${envFilePath}; set +a; mikan --sandbox=${sandboxSpec}`);
|
|
177
|
+
io.print(" mikan pulls the sandbox image on start.");
|
|
176
178
|
return 0;
|
|
177
179
|
}
|
|
178
180
|
export async function runOnboardCommand(stateDir) {
|
package/dist/cli/onboard.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboard.js","sourceRoot":"","sources":["../../src/cli/onboard.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport * as prompts from \"@clack/prompts\";\nimport { ENV_MANIFEST, envSetReport, readEnv } from \"../env-manifest.js\";\nimport { createGlobalSettingsFile } from \"../settings/index.js\";\nimport { recordAllMigrations } from \"../migrations/index.js\";\nimport type { OnboardLlmChoice } from \"../types.js\";\nimport { atomicWritePrivateFile } from \"../file-guards.js\";\nimport {\n MINIMAL_SANDBOX_IMAGE,\n PM2_ECOSYSTEM_FILENAME,\n PUBLISHED_SANDBOX_IMAGE,\n renderPm2Ecosystem,\n} from \"./pm2-ecosystem.js\";\nimport type { OnboardIo } from \"./types.js\";\n\nclass OnboardAborted extends Error {\n constructor() {\n super(\"Onboarding aborted.\");\n }\n}\n\nasync function answer<T>(result: Promise<T>): Promise<Exclude<T, symbol>> {\n const value = await result;\n if (prompts.isCancel(value)) throw new OnboardAborted();\n return value as Exclude<T, symbol>;\n}\n\nfunction terminalIo(): OnboardIo {\n return {\n ask: (message) => answer(prompts.text({ message: message.trim() })),\n askSecret: (message) => answer(prompts.password({ message: message.trim() })),\n select: (message, labels) =>\n answer(\n prompts.select({\n message,\n options: labels.map((label, value) => ({ label, value })),\n }),\n ),\n confirm: (message) => answer(prompts.confirm({ message, initialValue: true })),\n print: (line) => prompts.log.info(line.trim()),\n close: () => {},\n };\n}\n\nasync function askRequired(io: OnboardIo, query: string, secret = false): Promise<string> {\n for (;;) {\n const value = (secret ? await io.askSecret(query) : await io.ask(query)).trim();\n if (value) return value;\n io.print(\" (required)\");\n }\n}\n\nasync function askChoice(io: OnboardIo, labels: string[], prompt: string): Promise<number> {\n return io.select(prompt, labels);\n}\n\nasync function askAdapter(\n io: OnboardIo,\n env: Record<string, string>,\n): Promise<{ githubRepos?: string[] }> {\n const platforms = ENV_MANIFEST.filter((group) => group.kind === \"platform\");\n io.print(\"\\nStep 1/3 — chat adapter\");\n const index = await askChoice(\n io,\n platforms.map((g) => g.title),\n \"Platform\",\n );\n const group = platforms[index]!;\n for (const spec of group.vars.filter((v) => v.required)) {\n env[spec.name] = await askRequired(io, ` ${spec.name} (${spec.doc}): `, spec.secret);\n }\n if (group.key !== \"github\") return {};\n const repos = await askRequired(io, \" Repositories (owner/repo or owner/*, comma-separated): \");\n return {\n githubRepos: repos\n .split(\",\")\n .map((repo) => repo.trim())\n .filter(Boolean),\n };\n}\n\nasync function askLlm(\n io: OnboardIo,\n env: Record<string, string>,\n): Promise<{ llm: OnboardLlmChoice; modelsJson?: string }> {\n io.print(\"\\nStep 2/3 — LLM provider\");\n const index = await askChoice(\n io,\n [\n \"Anthropic (ANTHROPIC_API_KEY)\",\n \"OpenAI (OPENAI_API_KEY)\",\n \"Custom OpenAI-compatible endpoint (vLLM, proxy, …) — writes models.json\",\n ],\n \"Provider\",\n );\n if (index === 0) {\n env.ANTHROPIC_API_KEY = await askRequired(io, \" ANTHROPIC_API_KEY: \", true);\n return { llm: { provider: \"anthropic\", model: \"claude-sonnet-4-6\" } };\n }\n if (index === 1) {\n env.OPENAI_API_KEY = await askRequired(io, \" OPENAI_API_KEY: \", true);\n return { llm: { provider: \"openai\", model: \"gpt-5.2\" } };\n }\n const provider = (await io.ask(\" Provider name [custom]: \")).trim() || \"custom\";\n const baseUrl = await askRequired(io, \" Base URL (e.g. http://host:8080/v1): \");\n const apiKey = (await io.askSecret(\" API key (empty if none): \")).trim();\n const ids = (await askRequired(io, \" Model ids (comma-separated): \"))\n .split(\",\")\n .map((id) => id.trim())\n .filter(Boolean);\n const modelsJson = JSON.stringify(\n {\n providers: {\n [provider]: {\n api: \"openai-completions\",\n baseUrl,\n apiKey: apiKey ? apiKey : undefined,\n models: ids.map((id) => ({ id })),\n },\n },\n },\n null,\n 2,\n );\n return { llm: { provider, model: ids[0]! }, modelsJson };\n}\n\nasync function askSandboxImage(io: OnboardIo): Promise<string> {\n io.print(\"\\nStep 3/3 — sandbox image (one Docker container per conversation; needs Docker)\");\n const index = await askChoice(\n io,\n [\n `${PUBLISHED_SANDBOX_IMAGE} (recommended: git, gh, node, python, curl)`,\n `${MINIMAL_SANDBOX_IMAGE} (minimal: shell only, no git or package installs)`,\n \"Another image from Docker Hub or a registry…\",\n ],\n \"Sandbox image\",\n );\n if (index === 0) return PUBLISHED_SANDBOX_IMAGE;\n if (index === 1) return MINIMAL_SANDBOX_IMAGE;\n return askRequired(io, \" Image (e.g. registry/name:tag): \");\n}\n\nexport function renderEnvFile(existing: string | undefined, vars: Record<string, string>): string {\n const pending = new Map(Object.entries(vars));\n const lines: string[] = [];\n for (const line of existing ? existing.split(\"\\n\") : []) {\n const rendered = rewriteEnvLine(line, pending);\n if (rendered.trim() || lines.length > 0) lines.push(rendered);\n }\n while (lines.at(-1) === \"\") lines.pop();\n for (const [key, value] of pending) lines.push(`${key}=${value}`);\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nfunction rewriteEnvLine(line: string, pending: Map<string, string>): string {\n const key = /^([A-Za-z_][A-Za-z0-9_]*)=/.exec(line)?.[1];\n if (key === undefined || !pending.has(key)) return line;\n const value = pending.get(key);\n pending.delete(key);\n return `${key}=${value}`;\n}\n\nexport async function runOnboardWizard(\n stateDir: string,\n io: OnboardIo,\n paths?: { envFilePath?: string },\n): Promise<number> {\n const settingsPath = join(stateDir, \"settings.json\");\n if (existsSync(settingsPath)) {\n io.print(`Global settings already exists at ${settingsPath}.`);\n io.print(\"Remove it first to re-run onboarding, or edit it directly.\");\n return 1;\n }\n\n const env: Record<string, string> = {};\n const { githubRepos } = await askAdapter(io, env);\n const { llm, modelsJson } = await askLlm(io, env);\n const image = await askSandboxImage(io);\n\n const modelsPath = join(stateDir, \"models.json\");\n if (modelsJson && existsSync(modelsPath)) {\n io.print(\n `models.json already exists at ${modelsPath}. Add the provider manually before retrying; nothing was written.`,\n );\n return 1;\n }\n io.print(\n `Review settings\\nProvider: ${llm.provider}\\nModel: ${llm.model}\\nSandbox: image:${image}\\nSettings: ${settingsPath}\\nCredentials: ${paths?.envFilePath ?? join(stateDir, \"mikan.env\")}${modelsJson ? `\\nModels: ${modelsPath}` : \"\"}\\nSecrets are hidden.`,\n );\n if (!(await io.confirm(\"Save these settings?\"))) {\n io.print(\"Onboarding cancelled; nothing was written.\");\n return 1;\n }\n\n createGlobalSettingsFile(stateDir, { llm, githubRepos });\n recordAllMigrations(stateDir);\n io.print(`\\nWrote ${settingsPath}`);\n\n if (modelsJson) {\n atomicWritePrivateFile(modelsPath, `${modelsJson}\\n`);\n io.print(`Wrote ${modelsPath}`);\n }\n\n const envFilePath = paths?.envFilePath ?? join(stateDir, \"mikan.env\");\n const existing = existsSync(envFilePath) ? readFileSync(envFilePath, \"utf-8\") : undefined;\n atomicWritePrivateFile(envFilePath, renderEnvFile(existing, env));\n io.print(`Wrote ${envFilePath} (0600)`);\n\n io.print(`\\n${envSetReport((name) => env[name] ?? readEnv(name))}`);\n\n const ecosystemPath = join(stateDir, PM2_ECOSYSTEM_FILENAME);\n if (existsSync(ecosystemPath)) {\n io.print(`Kept existing ${ecosystemPath}`);\n } else {\n atomicWritePrivateFile(ecosystemPath, renderPm2Ecosystem(image));\n io.print(`Wrote ${ecosystemPath}`);\n }\n\n io.print(\"Next steps:\");\n io.print(` 1. docker pull ${image}`);\n io.print(` 2. pm2 start ${ecosystemPath} && pm2 save`);\n io.print(\n ` Foreground instead: set -a; source ${envFilePath}; set +a; mikan --sandbox=image:${image}`,\n );\n return 0;\n}\n\nexport async function runOnboardCommand(stateDir: string): Promise<number> {\n if (!process.stdin.isTTY) {\n const settingsPath = createGlobalSettingsFile(stateDir);\n recordAllMigrations(stateDir);\n console.log(`Created global settings at ${settingsPath}`);\n console.log(\n `Review the file, then start mikan (workspace defaults to ${join(stateDir, \"workspace\")}).`,\n );\n return 0;\n }\n prompts.intro(\"Mikan setup · arrows to choose · Esc / Ctrl+C to cancel\");\n const io = terminalIo();\n try {\n const code = await runOnboardWizard(stateDir, io);\n prompts.outro(code === 0 ? \"Setup complete\" : \"Setup not saved\");\n return code;\n } catch (err) {\n if (err instanceof OnboardAborted) {\n prompts.cancel(\"Onboarding aborted; nothing was written.\");\n return 1;\n }\n throw err;\n } finally {\n io.close();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"onboard.js","sourceRoot":"","sources":["../../src/cli/onboard.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport * as prompts from \"@clack/prompts\";\nimport { ENV_MANIFEST, envSetReport, readEnv } from \"../env-manifest.js\";\nimport { createGlobalSettingsFile } from \"../settings/index.js\";\nimport { recordAllMigrations } from \"../migrations/index.js\";\nimport type { OnboardLlmChoice } from \"../types.js\";\nimport { atomicWritePrivateFile } from \"../file-guards.js\";\nimport {\n MINIMAL_SANDBOX_IMAGE,\n PM2_ECOSYSTEM_FILENAME,\n renderPm2Ecosystem,\n} from \"./pm2-ecosystem.js\";\nimport { PUBLISHED_SANDBOX_SPEC } from \"../sandbox/registry.js\";\nimport { publishedSandboxImage } from \"../sandbox/published-image.js\";\nimport type { OnboardIo } from \"./types.js\";\n\nclass OnboardAborted extends Error {\n constructor() {\n super(\"Onboarding aborted.\");\n }\n}\n\nasync function answer<T>(result: Promise<T>): Promise<Exclude<T, symbol>> {\n const value = await result;\n if (prompts.isCancel(value)) throw new OnboardAborted();\n return value as Exclude<T, symbol>;\n}\n\nfunction terminalIo(): OnboardIo {\n return {\n ask: (message) => answer(prompts.text({ message: message.trim() })),\n askSecret: (message) => answer(prompts.password({ message: message.trim() })),\n select: (message, labels) =>\n answer(\n prompts.select({\n message,\n options: labels.map((label, value) => ({ label, value })),\n }),\n ),\n confirm: (message) => answer(prompts.confirm({ message, initialValue: true })),\n print: (line) => prompts.log.info(line.trim()),\n close: () => {},\n };\n}\n\nasync function askRequired(io: OnboardIo, query: string, secret = false): Promise<string> {\n for (;;) {\n const value = (secret ? await io.askSecret(query) : await io.ask(query)).trim();\n if (value) return value;\n io.print(\" (required)\");\n }\n}\n\nasync function askChoice(io: OnboardIo, labels: string[], prompt: string): Promise<number> {\n return io.select(prompt, labels);\n}\n\nasync function askAdapter(\n io: OnboardIo,\n env: Record<string, string>,\n): Promise<{ githubRepos?: string[] }> {\n const platforms = ENV_MANIFEST.filter((group) => group.kind === \"platform\");\n io.print(\"\\nStep 1/3 — chat adapter\");\n const index = await askChoice(\n io,\n platforms.map((g) => g.title),\n \"Platform\",\n );\n const group = platforms[index]!;\n for (const spec of group.vars.filter((v) => v.required)) {\n env[spec.name] = await askRequired(io, ` ${spec.name} (${spec.doc}): `, spec.secret);\n }\n if (group.key !== \"github\") return {};\n const repos = await askRequired(io, \" Repositories (owner/repo or owner/*, comma-separated): \");\n return {\n githubRepos: repos\n .split(\",\")\n .map((repo) => repo.trim())\n .filter(Boolean),\n };\n}\n\nasync function askLlm(\n io: OnboardIo,\n env: Record<string, string>,\n): Promise<{ llm: OnboardLlmChoice; modelsJson?: string }> {\n io.print(\"\\nStep 2/3 — LLM provider\");\n const index = await askChoice(\n io,\n [\n \"Anthropic (ANTHROPIC_API_KEY)\",\n \"OpenAI (OPENAI_API_KEY)\",\n \"Custom OpenAI-compatible endpoint (vLLM, proxy, …) — writes models.json\",\n ],\n \"Provider\",\n );\n if (index === 0) {\n env.ANTHROPIC_API_KEY = await askRequired(io, \" ANTHROPIC_API_KEY: \", true);\n return { llm: { provider: \"anthropic\", model: \"claude-sonnet-4-6\" } };\n }\n if (index === 1) {\n env.OPENAI_API_KEY = await askRequired(io, \" OPENAI_API_KEY: \", true);\n return { llm: { provider: \"openai\", model: \"gpt-5.2\" } };\n }\n const provider = (await io.ask(\" Provider name [custom]: \")).trim() || \"custom\";\n const baseUrl = await askRequired(io, \" Base URL (e.g. http://host:8080/v1): \");\n const apiKey = (await io.askSecret(\" API key (empty if none): \")).trim();\n const ids = (await askRequired(io, \" Model ids (comma-separated): \"))\n .split(\",\")\n .map((id) => id.trim())\n .filter(Boolean);\n const modelsJson = JSON.stringify(\n {\n providers: {\n [provider]: {\n api: \"openai-completions\",\n baseUrl,\n apiKey: apiKey ? apiKey : undefined,\n models: ids.map((id) => ({ id })),\n },\n },\n },\n null,\n 2,\n );\n return { llm: { provider, model: ids[0]! }, modelsJson };\n}\n\nasync function askSandboxSpec(io: OnboardIo): Promise<string> {\n io.print(\n \"\\nStep 3/3 — sandbox image (one container per conversation; needs nerdctl, Podman, or Docker)\",\n );\n const index = await askChoice(\n io,\n [\n `${publishedSandboxImage()} (recommended: git, gh, gcloud, node, python, browser; follows mikan upgrades)`,\n `${MINIMAL_SANDBOX_IMAGE} (minimal: shell only, no git or package installs)`,\n \"Another image from Docker Hub or a registry…\",\n ],\n \"Sandbox image\",\n );\n if (index === 0) return PUBLISHED_SANDBOX_SPEC;\n if (index === 1) return `oci:${MINIMAL_SANDBOX_IMAGE}`;\n return `oci:${await askRequired(io, \" Image (e.g. registry/name:tag): \")}`;\n}\n\nexport function renderEnvFile(existing: string | undefined, vars: Record<string, string>): string {\n const pending = new Map(Object.entries(vars));\n const lines: string[] = [];\n for (const line of existing ? existing.split(\"\\n\") : []) {\n const rendered = rewriteEnvLine(line, pending);\n if (rendered.trim() || lines.length > 0) lines.push(rendered);\n }\n while (lines.at(-1) === \"\") lines.pop();\n for (const [key, value] of pending) lines.push(`${key}=${value}`);\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nfunction rewriteEnvLine(line: string, pending: Map<string, string>): string {\n const key = /^([A-Za-z_][A-Za-z0-9_]*)=/.exec(line)?.[1];\n if (key === undefined || !pending.has(key)) return line;\n const value = pending.get(key);\n pending.delete(key);\n return `${key}=${value}`;\n}\n\nexport async function runOnboardWizard(\n stateDir: string,\n io: OnboardIo,\n paths?: { envFilePath?: string },\n): Promise<number> {\n const settingsPath = join(stateDir, \"settings.json\");\n if (existsSync(settingsPath)) {\n io.print(`Global settings already exists at ${settingsPath}.`);\n io.print(\"Remove it first to re-run onboarding, or edit it directly.\");\n return 1;\n }\n\n const env: Record<string, string> = {};\n const { githubRepos } = await askAdapter(io, env);\n const { llm, modelsJson } = await askLlm(io, env);\n const sandboxSpec = await askSandboxSpec(io);\n\n const modelsPath = join(stateDir, \"models.json\");\n if (modelsJson && existsSync(modelsPath)) {\n io.print(\n `models.json already exists at ${modelsPath}. Add the provider manually before retrying; nothing was written.`,\n );\n return 1;\n }\n io.print(\n `Review settings\\nProvider: ${llm.provider}\\nModel: ${llm.model}\\nSandbox: ${sandboxSpec}\\nSettings: ${settingsPath}\\nCredentials: ${paths?.envFilePath ?? join(stateDir, \"mikan.env\")}${modelsJson ? `\\nModels: ${modelsPath}` : \"\"}\\nSecrets are hidden.`,\n );\n if (!(await io.confirm(\"Save these settings?\"))) {\n io.print(\"Onboarding cancelled; nothing was written.\");\n return 1;\n }\n\n createGlobalSettingsFile(stateDir, { llm, githubRepos });\n recordAllMigrations(stateDir);\n io.print(`\\nWrote ${settingsPath}`);\n\n if (modelsJson) {\n atomicWritePrivateFile(modelsPath, `${modelsJson}\\n`);\n io.print(`Wrote ${modelsPath}`);\n }\n\n const envFilePath = paths?.envFilePath ?? join(stateDir, \"mikan.env\");\n const existing = existsSync(envFilePath) ? readFileSync(envFilePath, \"utf-8\") : undefined;\n atomicWritePrivateFile(envFilePath, renderEnvFile(existing, env));\n io.print(`Wrote ${envFilePath} (0600)`);\n\n io.print(`\\n${envSetReport((name) => env[name] ?? readEnv(name))}`);\n\n const ecosystemPath = join(stateDir, PM2_ECOSYSTEM_FILENAME);\n if (existsSync(ecosystemPath)) {\n io.print(`Kept existing ${ecosystemPath}`);\n } else {\n atomicWritePrivateFile(ecosystemPath, renderPm2Ecosystem(sandboxSpec));\n io.print(`Wrote ${ecosystemPath}`);\n }\n\n io.print(\"Next steps:\");\n io.print(` pm2 start ${ecosystemPath} && pm2 save`);\n io.print(\n ` Foreground instead: set -a; source ${envFilePath}; set +a; mikan --sandbox=${sandboxSpec}`,\n );\n io.print(\" mikan pulls the sandbox image on start.\");\n return 0;\n}\n\nexport async function runOnboardCommand(stateDir: string): Promise<number> {\n if (!process.stdin.isTTY) {\n const settingsPath = createGlobalSettingsFile(stateDir);\n recordAllMigrations(stateDir);\n console.log(`Created global settings at ${settingsPath}`);\n console.log(\n `Review the file, then start mikan (workspace defaults to ${join(stateDir, \"workspace\")}).`,\n );\n return 0;\n }\n prompts.intro(\"Mikan setup · arrows to choose · Esc / Ctrl+C to cancel\");\n const io = terminalIo();\n try {\n const code = await runOnboardWizard(stateDir, io);\n prompts.outro(code === 0 ? \"Setup complete\" : \"Setup not saved\");\n return code;\n } catch (err) {\n if (err instanceof OnboardAborted) {\n prompts.cancel(\"Onboarding aborted; nothing was written.\");\n return 1;\n }\n throw err;\n } finally {\n io.close();\n }\n}\n"]}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export declare const PUBLISHED_SANDBOX_IMAGE = "ghcr.io/geminixiang/mikan-sandbox:latest";
|
|
2
1
|
export declare const MINIMAL_SANDBOX_IMAGE = "debian:trixie-slim";
|
|
3
2
|
export declare const PM2_ECOSYSTEM_FILENAME = "ecosystem.config.cjs";
|
|
4
|
-
export declare function renderPm2Ecosystem(
|
|
3
|
+
export declare function renderPm2Ecosystem(sandboxSpec: string): string;
|
|
5
4
|
//# sourceMappingURL=pm2-ecosystem.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pm2-ecosystem.d.ts","sourceRoot":"","sources":["../../src/cli/pm2-ecosystem.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"pm2-ecosystem.d.ts","sourceRoot":"","sources":["../../src/cli/pm2-ecosystem.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,uBAAuB,CAAC;AAE1D,eAAO,MAAM,sBAAsB,yBAAyB,CAAC;AAE7D,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAqD9D"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
export const PUBLISHED_SANDBOX_IMAGE = "ghcr.io/geminixiang/mikan-sandbox:latest";
|
|
2
1
|
export const MINIMAL_SANDBOX_IMAGE = "debian:trixie-slim";
|
|
3
2
|
export const PM2_ECOSYSTEM_FILENAME = "ecosystem.config.cjs";
|
|
4
|
-
export function renderPm2Ecosystem(
|
|
3
|
+
export function renderPm2Ecosystem(sandboxSpec) {
|
|
5
4
|
return `const fs = require("node:fs");
|
|
6
5
|
const os = require("node:os");
|
|
7
6
|
const path = require("node:path");
|
|
@@ -38,7 +37,7 @@ module.exports = {
|
|
|
38
37
|
name: "mikan",
|
|
39
38
|
script: "mikan",
|
|
40
39
|
|
|
41
|
-
args: ${JSON.stringify(`--sandbox
|
|
40
|
+
args: ${JSON.stringify(`--sandbox=${sandboxSpec}`)},
|
|
42
41
|
|
|
43
42
|
env: loadEnvFile(path.join(os.homedir(), ".mikan", "mikan.env")),
|
|
44
43
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pm2-ecosystem.js","sourceRoot":"","sources":["../../src/cli/pm2-ecosystem.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"pm2-ecosystem.js","sourceRoot":"","sources":["../../src/cli/pm2-ecosystem.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,qBAAqB,GAAG,oBAAoB,CAAC;AAE1D,MAAM,CAAC,MAAM,sBAAsB,GAAG,sBAAsB,CAAC;AAE7D,MAAM,UAAU,kBAAkB,CAAC,WAAmB;IACpD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAoCK,IAAI,CAAC,SAAS,CAAC,aAAa,WAAW,EAAE,CAAC;;;;;;;;;;;;;;;CAevD,CAAC;AACF,CAAC","sourcesContent":["export const MINIMAL_SANDBOX_IMAGE = \"debian:trixie-slim\";\n\nexport const PM2_ECOSYSTEM_FILENAME = \"ecosystem.config.cjs\";\n\nexport function renderPm2Ecosystem(sandboxSpec: string): string {\n return `const fs = require(\"node:fs\");\nconst os = require(\"node:os\");\nconst path = require(\"node:path\");\n\nfunction loadEnvFile(file) {\n let text;\n try {\n text = fs.readFileSync(file, \"utf-8\");\n } catch {\n return {};\n }\n const env = {};\n for (const line of text.split(\"\\\\n\")) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n const eq = trimmed.indexOf(\"=\");\n if (eq <= 0) continue;\n const key = trimmed.slice(0, eq).trim();\n let value = trimmed.slice(eq + 1).trim();\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n env[key] = value;\n }\n return env;\n}\n\nmodule.exports = {\n apps: [\n {\n name: \"mikan\",\n script: \"mikan\",\n\n args: ${JSON.stringify(`--sandbox=${sandboxSpec}`)},\n\n env: loadEnvFile(path.join(os.homedir(), \".mikan\", \"mikan.env\")),\n\n kill_timeout: 360000,\n\n autorestart: true,\n max_restarts: 10,\n restart_delay: 2000,\n\n time: true,\n merge_logs: true,\n },\n ],\n};\n`;\n}\n"]}
|