elanous 0.2.7 → 0.2.9
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/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/bFI5EEwg7T6aG88ibX4Zq/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7032-f4ef698518fba61c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8067-d976b441a104ab74.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8519-5632377cce994f86.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8630-cdfc61e82c653ffc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8894-e263b332208e3957.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9454-139040d37c79dbc9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9740-8f5ff96c522b5f55.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-fb7c2d458f32fcad.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-35e2713597dd24d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-918b656f064577ba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-624f7a2f89b60c8f.js → page-f972fc1c00a62e88.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-5a5fb385a4df0ffe.js → page-5f30e00693527ebd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-80497e93a878ddc5.js → page-7fbae173b3c103a7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/{page-7457f52683562161.js → page-3b4edcce364062ea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-c5794f484c251f44.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-1130d8eefcd87e76.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-973996d584f008c1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-b7dcf5d48a854b76.js → page-016e2bda04eecb1f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-51480757e7d68b92.js → page-b4d6d9a3b56f2940.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-ec49a466121233ba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-f72db356744ceade.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-a6fd9b50d1b85378.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-8b0402a46a749a3d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-07267f1391b96aba.js → page-037f83e2a1edc8e2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-55d50e9b0006a91d.js → page-adfbd2ba9598e59a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-46fedff57d4bec67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-0cf318e9dcee2bbf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-899b327cb8b6845c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-6c1e6ba743a29dfe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/{page-a7e37af7d0399cb8.js → page-1a8e4807292f6b17.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-28deba6bcb834916.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-de68e2205794037d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-15e5a15db84c56aa.js → page-c71c33a573230672.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-a2e572e4511f2a16.js → page-0df30e0c7e50148a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-4fc56790fe0d2682.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-59df2aebb38a7c1e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-4647be5dbcbd0795.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-e025892bda74b743.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a0955de73b89f078.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-c1c95739c0bff3b8.js → page-369846d2921af584.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-72cbc1c8cdd0f65d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-71d18eec33b9c667.js → page-77b28bfb0996d5ea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-3220c4934f49c6c2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-2c4e59079ee357ac.js → page-90670589b87abea8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-7df9243ed021a4cc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-5d422f38c052967e.js → page-4498c3853f4f688d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-35bedde6d885f08f.js +1 -0
- package/apps/pwa/out/_next/static/css/f92f63a5df0ba229.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +30 -29
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +30 -29
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +30 -29
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +27 -26
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +27 -26
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +27 -26
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +30 -29
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +30 -29
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +30 -29
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +30 -29
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +30 -29
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +30 -29
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +27 -26
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +30 -29
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +30 -29
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +27 -26
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +27 -26
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +30 -29
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +30 -29
- package/apps/pwa/out/ops/checklist/index.html +1 -0
- package/apps/pwa/out/ops/checklist/index.txt +30 -0
- package/apps/pwa/out/ops/release/index.html +1 -0
- package/apps/pwa/out/ops/release/index.txt +30 -0
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +28 -27
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +30 -29
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +27 -26
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +30 -29
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +30 -29
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +30 -29
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +30 -29
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +30 -29
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +27 -26
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +30 -29
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +30 -29
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +30 -29
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +30 -29
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +30 -29
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +30 -29
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +30 -29
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +30 -29
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +3 -1
- package/graphs/test-diet/nightly-audit.yaml +18 -0
- package/graphs/test-diet/recipes.yaml +12 -0
- package/graphs/test-diet/test-diet.yaml +22 -0
- package/package.json +2 -1
- package/persona-presets/_index.yaml +10 -0
- package/persona-presets/_schema.json +146 -0
- package/persona-presets/dana.yaml +26 -0
- package/persona-presets/haru.yaml +27 -0
- package/persona-presets/kai.yaml +27 -0
- package/persona-presets/leo.yaml +27 -0
- package/persona-presets/mira.yaml +30 -0
- package/persona-presets/nari.yaml +27 -0
- package/persona-presets/sora.yaml +27 -0
- package/persona-presets/yuna.yaml +27 -0
- package/plugins/doc-draft/graphs/run-step.ts +1 -1
- package/plugins/geo-check/graphs/run-step.ts +34 -10
- package/scripts/field-feed/deliver-node.ts +8 -3
- package/scripts/field-feed/lib.ts +2 -0
- package/scripts/install.sh +20 -11
- package/scripts/persona-presets/lint.ts +79 -0
- package/scripts/release-loop/checklist-gate-node.ts +2 -2
- package/scripts/release-loop/cutoff.ts +20 -2
- package/scripts/release-loop/export-check-node.ts +85 -0
- package/scripts/release-loop/gate-node.ts +45 -7
- package/scripts/release-loop/node-verdict.ts +8 -1
- package/scripts/release-loop/npm-publish-node.ts +10 -4
- package/scripts/release-loop/ops-upgrade-node.ts +11 -3
- package/scripts/release-loop/publish-node.ts +23 -1
- package/scripts/release-loop/shard-plan.ts +17 -0
- package/scripts/storyboard/storyboard.ts +167 -0
- package/scripts/test-diet/lib.ts +89 -0
- package/scripts/test-diet/node.ts +144 -0
- package/src/acp/auth-methods.ts +30 -0
- package/src/acp/server.ts +5 -5
- package/src/auth/setup-link-tokens.fixture.ts +19 -0
- package/src/auth/setup-link-tokens.ts +153 -0
- package/src/budget/unified-usage.ts +75 -0
- package/src/channel-bot-token.ts +37 -10
- package/src/channel-import/detect.ts +191 -0
- package/src/channel-import/run.ts +111 -0
- package/src/chat/index.ts +9 -9
- package/src/cli/config-test-sync.ts +4 -0
- package/src/cli/decisions-cli.ts +13 -3
- package/src/cli/doctor-bundle.ts +128 -0
- package/src/cli/doctor-cli.ts +36 -1
- package/src/cli/doctor-local-llm.ts +95 -0
- package/src/cli/import-cli.ts +60 -0
- package/src/cli/mcp-login.ts +13 -4
- package/src/cli/op-proxy-sync.ts +83 -0
- package/src/cli/persona-cli.ts +116 -0
- package/src/cli/pr-cli.ts +6 -2
- package/src/cli/release-cli.ts +49 -8
- package/src/cli/skills-cli.ts +173 -0
- package/src/cli/start-cli.ts +13 -4
- package/src/codex/setup.ts +15 -13
- package/src/dashboard/dashboard-status-lines.ts +45 -0
- package/src/dashboard/first-screen-band.ts +35 -0
- package/src/dashboard/index.ts +87 -14
- package/src/dashboard/provider-slash-runtime.ts +28 -11
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +12 -2
- package/src/dashboard/slash-runtime/help-from-registry.ts +4 -2
- package/src/dashboard/turn-stream-formatter.ts +1 -1
- package/src/decisions/decision-cards.ts +291 -0
- package/src/decisions/decision-ledger.ts +5 -2
- package/src/decisions/discord-decision-cards.ts +133 -0
- package/src/decisions/telegram-decision-cards.ts +100 -0
- package/src/directives/version-at.ts +77 -0
- package/src/discord.ts +17 -0
- package/src/domains/outbound-alert.ts +44 -1
- package/src/domains/repeated-failure.ts +1 -1
- package/src/error-report/report.ts +200 -0
- package/src/exec-requests/planner.ts +8 -2
- package/src/exec-requests/runner.ts +64 -24
- package/src/exec-requests/store.ts +2 -0
- package/src/field/field-feed-auto.ts +48 -0
- package/src/field/field-reel.ts +8 -1
- package/src/hooks/error-report.ts +210 -0
- package/src/hooks/receiver.ts +41 -1
- package/src/import/apply.ts +128 -0
- package/src/import/plan.ts +197 -0
- package/src/index.ts +98 -11
- package/src/input-core/text-entry.ts +13 -5
- package/src/intake-plane/seat-answer.ts +96 -0
- package/src/intake-plane/seat-doc-route.ts +17 -3
- package/src/maturity/feature-maturity.ts +2 -0
- package/src/nexus/api/consult-requests.ts +19 -8
- package/src/nexus/api/error-reports.ts +108 -0
- package/src/nexus/api/http-server.ts +41 -1
- package/src/nexus/api/meta-api.ts +27 -0
- package/src/nexus/api/operator.ts +61 -0
- package/src/nexus/api/ops-api.ts +139 -0
- package/src/nexus/api/ops-seats-sources.ts +65 -0
- package/src/nexus/api/ops-seats.ts +114 -0
- package/src/nexus/api/outbound-report.ts +7 -0
- package/src/nexus/api/public-routes.ts +14 -1
- package/src/nexus/api/running-runs-background.ts +68 -0
- package/src/nexus/api/running-runs-child.ts +6 -0
- package/src/nexus/api/setup-claim.ts +21 -0
- package/src/nexus/api/share-captures.ts +91 -0
- package/src/nexus/api/skill-problems.ts +56 -0
- package/src/nexus/api/terminals.ts +24 -8
- package/src/nexus/config/secrets/file-backend.ts +56 -18
- package/src/nexus/index.ts +25 -1
- package/src/nexus/setup-status.ts +7 -2
- package/src/oauth/browser-availability.ts +14 -0
- package/src/oauth/codex-quota-policy.ts +16 -0
- package/src/onboarding/continue-here.ts +13 -8
- package/src/onboarding/first-run-doctor.ts +54 -0
- package/src/onboarding/web-first.ts +8 -1
- package/src/onboarding.ts +66 -12
- package/src/persona/presets.ts +105 -0
- package/src/release-loop/checklist.ts +33 -69
- package/src/release-loop/feature-store.ts +327 -0
- package/src/release-loop/manifest.ts +59 -5
- package/src/seat-dispatch/ceo-commands.ts +65 -0
- package/src/self-dev/orchestrate.ts +18 -11
- package/src/shell-env-bootstrap.ts +44 -7
- package/src/skills/connect-claude-plugin.ts +142 -0
- package/src/skills/connect.ts +70 -0
- package/src/skills/index.ts +29 -1
- package/src/skills/repair.ts +124 -0
- package/src/skills/runner.ts +11 -2
- package/src/skills/tools/usage-slash.ts +8 -7
- package/src/skills/trigger-extract.ts +1 -1
- package/src/task-orchestrator/surfaces/pod-command-job.ts +16 -8
- package/src/telegram-commands.ts +29 -0
- package/src/telegram-run.ts +83 -13
- package/src/telegram.ts +34 -0
- package/src/thinking-line.ts +28 -20
- package/src/tui/chat/execution-badge.ts +1 -1
- package/src/user-config.ts +111 -13
- package/src/ux-sim/turn-states.ts +1 -1
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/4836-2eba6079f31b88d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8067-f74bf14d67ad98f1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8519-f6b5d8a5833a7603.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8894-e92491a164192cc1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9454-a3e0953307216c58.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9740-67051141115ce775.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9804-64c02c3c127d5014.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-d5ebde04844689e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-10ed24f413ed66e9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-211810b6c93ab66a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-cee86394de407aa6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-4ff8edab44196b8c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-92e4f44428d5e290.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-2a05dfe80c8bdf16.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-d9979633aa82f8e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-30533117beb53625.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-f7857826c2f97ad7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-9699b1786be6171d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-43dd772aee8af75f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-973ab7af59c718b7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-a9f8d3865b91e436.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-dd107bf1c7d52a90.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-d42b99cdfb9f27c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-2764ff40dad5a995.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-c5f1152db116ff96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-f933bca81ea38a6f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-a7aaffcf771662d8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-56adbe1a1f1379bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-a81f113f3ef89d72.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-fc27b00e20733146.js +0 -1
- package/apps/pwa/out/_next/static/css/f537bdb19c69306a.css +0 -1
- package/apps/pwa/out/_next/static/keajW7DDyghEs1y8oJe6T/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{keajW7DDyghEs1y8oJe6T → bFI5EEwg7T6aG88ibX4Zq}/_ssgManifest.js +0 -0
package/src/nexus/index.ts
CHANGED
|
@@ -2609,10 +2609,16 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
|
|
|
2609
2609
|
runTurnImpl,
|
|
2610
2610
|
getBot: () => getBotRef.current,
|
|
2611
2611
|
});
|
|
2612
|
+
// DEC-TG(대표 10-02) — decision cards in the owner's Discord DM. Filled once the bot exists (below).
|
|
2613
|
+
let decisionWire: import('../decisions/discord-decision-cards.js').DiscordDecisionWire | null = null;
|
|
2612
2614
|
const composedOnMessage = async (
|
|
2613
2615
|
ctx: import('../discord.js').DcIncoming,
|
|
2614
2616
|
streamer?: import('../discord.js').DcMessageStreamer,
|
|
2615
2617
|
): Promise<string | void> => {
|
|
2618
|
+
if (decisionWire && ctx.text.trim() === '/decisions') {
|
|
2619
|
+
const { discordDecisionsCommand } = await import('../decisions/discord-decision-cards.js');
|
|
2620
|
+
return discordDecisionsCommand(decisionWire.service, ctx);
|
|
2621
|
+
}
|
|
2616
2622
|
const voiceReply = await voiceWire.dispatchVoiceCommand(ctx);
|
|
2617
2623
|
if (voiceReply !== null) return voiceReply;
|
|
2618
2624
|
return selfOnMessage(ctx, streamer as never);
|
|
@@ -2635,7 +2641,8 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
|
|
|
2635
2641
|
dispatch: (event) => workflowDaemon!.dispatchDiscord(event),
|
|
2636
2642
|
onMessage: composedOnMessage,
|
|
2637
2643
|
onInteraction: async (raw) => {
|
|
2638
|
-
// C1 버튼 탭
|
|
2644
|
+
// DEC-TG 결정 카드(dec:/decmemo:) → C1 버튼 탭(elanous-q: 소비) → 아니면 C3 슬래시.
|
|
2645
|
+
if (decisionWire && await decisionWire.onInteraction(raw)) return;
|
|
2639
2646
|
if (await questionRuntime.handleComponentInteraction(raw)) return;
|
|
2640
2647
|
await slashWire.onInteraction(raw);
|
|
2641
2648
|
},
|
|
@@ -2644,6 +2651,14 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
|
|
|
2644
2651
|
if (handle) {
|
|
2645
2652
|
getBotRef.current = handle.bot;
|
|
2646
2653
|
workflowDiscordBot = handle;
|
|
2654
|
+
if ((cfg.raw?.decisions as { cards?: unknown } | undefined)?.cards !== false) {
|
|
2655
|
+
try {
|
|
2656
|
+
const { attachDiscordDecisionCards } = await import('../decisions/discord-decision-cards.js');
|
|
2657
|
+
decisionWire = attachDiscordDecisionCards(handle.bot, cfg);
|
|
2658
|
+
} catch (err) {
|
|
2659
|
+
console.warn(`[nexus] decision cards (discord) not attached: ${err instanceof Error ? err.message : String(err)}`);
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2647
2662
|
void slashWire.registerCommands().catch((err: unknown) => {
|
|
2648
2663
|
console.warn(`[nexus] slash registration failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2649
2664
|
});
|
|
@@ -2919,6 +2934,15 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
|
|
|
2919
2934
|
: readyCount === 1 ? readyServerIds[0] : undefined,
|
|
2920
2935
|
readyCount,
|
|
2921
2936
|
});
|
|
2937
|
+
// TERM1 ④ — the first web-terminal spawn used to pay the login-shell env capture (spawnSync · ~3 s with the
|
|
2938
|
+
// daemon frozen) and the terminal module import (~0.9 s). Warm both off the event loop shortly after boot.
|
|
2939
|
+
if (!opts.startNexusHttpServerFn) {
|
|
2940
|
+
const warm = setTimeout(() => {
|
|
2941
|
+
void import('../shell-env-bootstrap.js').then(({ warmCapturedEnv }) => warmCapturedEnv()).catch(() => undefined);
|
|
2942
|
+
void import('../web-terminal/preview-tap-registry.js').then(() => import('../preview/terminal.js')).catch(() => undefined);
|
|
2943
|
+
}, 1_500);
|
|
2944
|
+
(warm as { unref?: () => void }).unref?.();
|
|
2945
|
+
}
|
|
2922
2946
|
httpServer = startHttpServer({
|
|
2923
2947
|
state,
|
|
2924
2948
|
registry,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
|
|
3
3
|
import { debug } from '../debug/log.js';
|
|
4
|
-
import { buildUserConfig, type UserConfig as MainUserConfig } from '../user-config.js';
|
|
4
|
+
import { buildUserConfig, resolveSkillSources, type UserConfig as MainUserConfig } from '../user-config.js';
|
|
5
5
|
import { decideProviderForConfig } from '../llm.js';
|
|
6
6
|
import { resolveGrokCredential } from '../grok/credential.js';
|
|
7
7
|
import { loadTokens } from '../oauth/store.js';
|
|
@@ -177,7 +177,12 @@ function checkChannelBot(cfg: NexusUserConfig): SetupItem {
|
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
function checkSkillDirs(cfg: MainUserConfig, exists: (path: string) => boolean): SetupItem {
|
|
180
|
-
|
|
180
|
+
// EN5 — the same list the loader reads (resolveSkillSources): the preset and what `elanous connect` registered.
|
|
181
|
+
// Derived roots (shared ~/.agents/skills, Claude package roots) are optional extras, not «configured» dirs.
|
|
182
|
+
const dirs = resolveSkillSources(cfg)
|
|
183
|
+
.filter((source) => source.enabled && (source.kind === 'preset' || source.kind === 'connected'))
|
|
184
|
+
.map((source) => source.path)
|
|
185
|
+
.filter(hasText);
|
|
181
186
|
const existingDirs = dirs.filter(exists);
|
|
182
187
|
const missingDirs = dirs.filter((dir) => !exists(dir));
|
|
183
188
|
const detail = `${dirs.length} dir${dirs.length === 1 ? '' : 's'} · ${existingDirs.length} exist`;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// OB5d — 브라우저를 열 수 없는 세션이면 «왜 못 여는지 ⊕ 대안»을 한 줄로 말한다(대표 10-01 node-c 실측: ssh 에서 아무 말 없이 기기 코드로 갔다).
|
|
2
|
+
// null = 이 세션에서 브라우저 로그인을 먼저 시도해도 된다.
|
|
3
|
+
export function browserUnavailableReason(env: NodeJS.ProcessEnv = process.env, platform: NodeJS.Platform = process.platform): string | null {
|
|
4
|
+
const ssh = !!(env.SSH_CONNECTION || env.SSH_CLIENT || env.SSH_TTY);
|
|
5
|
+
if (platform === 'darwin' || platform === 'win32') {
|
|
6
|
+
if (!ssh) return null;
|
|
7
|
+
const where = platform === 'darwin' ? '이 Mac' : '이 PC';
|
|
8
|
+
return `원격(ssh) 접속이라 브라우저 로그인을 마칠 수 없습니다 — ${where} 화면의 터미널에서 같은 명령을 실행하면 브라우저가 바로 열립니다(붙여넣기 없이). 지금은 아래 코드로 로그인합니다.`;
|
|
9
|
+
}
|
|
10
|
+
if (env.DISPLAY || env.WAYLAND_DISPLAY) return null;
|
|
11
|
+
return ssh
|
|
12
|
+
? '원격(ssh) 접속이고 화면이 없어 브라우저를 열 수 없습니다 — 아무 기기의 브라우저에서 아래 주소를 열고 코드를 넣으세요.'
|
|
13
|
+
: '화면(디스플레이)이 없는 세션이라 브라우저를 열 수 없습니다 — 아무 기기의 브라우저에서 아래 주소를 열고 코드를 넣으세요.';
|
|
14
|
+
}
|
|
@@ -53,3 +53,19 @@ export function codexPolicyAllowsCredits(policy: CodexQuotaPolicy): boolean {
|
|
|
53
53
|
export function codexPolicyAllowsFallback(policy: CodexQuotaPolicy): boolean {
|
|
54
54
|
return policy !== 'within-quota';
|
|
55
55
|
}
|
|
56
|
+
|
|
57
|
+
/** POL1 — the one line a harness launch prints about the codex quota policy it will use, and a loud warning when a
|
|
58
|
+
* test universe disagrees with production (10-01: work-tree launches resolved «fallback» and leaked to grok). */
|
|
59
|
+
export function describeLaunchQuotaPolicy(input: {
|
|
60
|
+
current: ResolvedCodexQuotaPolicy;
|
|
61
|
+
universe: { kind: 'prod' | 'test'; root: string };
|
|
62
|
+
production?: ResolvedCodexQuotaPolicy;
|
|
63
|
+
}): { line: string; warning?: string } {
|
|
64
|
+
const { current, universe, production } = input;
|
|
65
|
+
const line = `[pod] codex 한도 정책 = ${current.policy}(${CODEX_QUOTA_POLICY_LABEL[current.policy]}) · 출처 ${current.source} · 우주 ${universe.kind} ${universe.root}`;
|
|
66
|
+
if (universe.kind === 'test' && production && production.policy !== current.policy) {
|
|
67
|
+
return { line, warning: `⚠️ 이 시험 우주의 한도 정책(${current.policy})이 운영(${production.policy})과 다르다 — 운영과 같게 하려면: bun bin/elanous.mjs config sync-test` };
|
|
68
|
+
}
|
|
69
|
+
return { line };
|
|
70
|
+
}
|
|
71
|
+
|
|
@@ -5,25 +5,30 @@ export interface ContinueHereOptions {
|
|
|
5
5
|
/** Already-issued short-lived phone connection link; never pass its token separately. */
|
|
6
6
|
phoneLink?: string;
|
|
7
7
|
telegramEnabled: boolean;
|
|
8
|
+
/** Tailscale signed in. `host` only when MagicDNS gives a name that serves HTTPS; otherwise the command alone. */
|
|
9
|
+
tailscale?: { host?: string };
|
|
8
10
|
/** Non-interactive output must not include links (or QR payloads). */
|
|
9
11
|
interactive: boolean;
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
/** Render post-setup directions without inspecting the daemon, issuing credentials, or performing IO. */
|
|
13
15
|
export function continueHereLines(opts: ContinueHereOptions): string[] {
|
|
14
|
-
const lines = ['
|
|
15
|
-
if (!opts.daemonRunning) lines.push('
|
|
16
|
+
const lines = [' 여기서 이어가세요:'];
|
|
17
|
+
if (!opts.daemonRunning) lines.push(' 데몬 켜기: `elanous nexus run`');
|
|
16
18
|
if (opts.interactive && opts.daemonRunning && opts.pwaUrl) {
|
|
17
|
-
lines.push(`
|
|
19
|
+
lines.push(` 브라우저: ${opts.pwaUrl}`);
|
|
18
20
|
} else {
|
|
19
|
-
lines.push('
|
|
21
|
+
lines.push(' 브라우저: `elanous nexus show`');
|
|
20
22
|
}
|
|
21
23
|
if (opts.interactive && opts.daemonRunning && opts.phoneLink) {
|
|
22
|
-
lines.push(`
|
|
23
|
-
lines.push(' ⚠
|
|
24
|
+
lines.push(` 폰: ${opts.phoneLink}`);
|
|
25
|
+
lines.push(' ⚠ 이 링크에는 연결 토큰이 들어 있습니다 — 남에게 보내거나 공개 캡처에 넣지 마세요.');
|
|
24
26
|
} else {
|
|
25
|
-
lines.push('
|
|
27
|
+
lines.push(' 폰: `elanous phone link --temp --ttl 24h`');
|
|
28
|
+
}
|
|
29
|
+
if (opts.telegramEnabled) lines.push(' 텔레그램: `elanous telegram`');
|
|
30
|
+
if (opts.tailscale) {
|
|
31
|
+
lines.push(` 어디서든(Tailscale): \`elanous nexus pwa share enable\`${opts.interactive && opts.tailscale.host ? ` → https://${opts.tailscale.host}/app/` : ''}`);
|
|
26
32
|
}
|
|
27
|
-
if (opts.telegramEnabled) lines.push(' Telegram: `elanous telegram`');
|
|
28
33
|
return lines;
|
|
29
34
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { delimiter, join, win32 } from 'node:path';
|
|
2
|
+
import { applyDoctorFixes, planDoctorFixes, type DoctorFixDeps, type DoctorFixItem } from '../cli/doctor-fix.js';
|
|
3
|
+
import { staticToolBinDir } from '../cli/doctor-static-tools.js';
|
|
4
|
+
import { debug } from '../debug/log.js';
|
|
5
|
+
|
|
6
|
+
const AUTOMATIC = new Set<DoctorFixItem['id']>(['private-files', 'key-cache-permissions', 'static-tools']);
|
|
7
|
+
const DEFERRED = new Set<DoctorFixItem['id']>(['install-path', 'bun-tmpdir']);
|
|
8
|
+
const LATER_LINE = '나중에: elanous doctor --fix (셸 시작 파일 수정)';
|
|
9
|
+
|
|
10
|
+
export interface FirstRunDoctorResult {
|
|
11
|
+
fixed: string[];
|
|
12
|
+
skipped: string[];
|
|
13
|
+
failed: string[];
|
|
14
|
+
later: string[];
|
|
15
|
+
lines: string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** 첫 실행에서는 동의가 필요 없는 수리만 실행하고, 셸 시작 파일은 손대지 않는다. */
|
|
19
|
+
export function runFirstRunDoctor(deps: DoctorFixDeps): FirstRunDoctorResult {
|
|
20
|
+
const result: FirstRunDoctorResult = { fixed: [], skipped: [], failed: [], later: [], lines: [] };
|
|
21
|
+
const record = (id: string, outcome: 'fixed' | 'skipped' | 'failed', reason?: string) => {
|
|
22
|
+
result[outcome].push(id);
|
|
23
|
+
if (outcome === 'failed') result.lines.push(`doctor: ${id} 수리 실패 — ${reason || '원인 불명'}`);
|
|
24
|
+
try { debug.log('doctor.first-run', outcome, { id }); } catch { /* 관측 실패가 첫 실행을 막지 않는다. */ }
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const windows = deps.readiness?.platform === 'win32';
|
|
29
|
+
const prefix = deps.readiness?.installPrefix;
|
|
30
|
+
const bin = typeof prefix === 'string' && prefix.trim()
|
|
31
|
+
? windows ? win32.join(prefix.trim(), 'bin') : join(prefix.trim(), 'bin')
|
|
32
|
+
: staticToolBinDir(deps.env ?? process.env, deps.home);
|
|
33
|
+
const separator = windows ? ';' : delimiter;
|
|
34
|
+
const entries = (process.env.PATH ?? '').split(separator);
|
|
35
|
+
if (!entries.some((entry) => windows ? entry.toLowerCase() === bin.toLowerCase() : entry === bin)) {
|
|
36
|
+
process.env.PATH = [bin, process.env.PATH].filter(Boolean).join(separator);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const plan = planDoctorFixes(deps);
|
|
40
|
+
for (const item of plan.items) {
|
|
41
|
+
if (DEFERRED.has(item.id) && item.status === 'fixable') result.later.push(item.id);
|
|
42
|
+
}
|
|
43
|
+
const applied = applyDoctorFixes(deps, true, AUTOMATIC);
|
|
44
|
+
for (const item of applied.items) {
|
|
45
|
+
if (AUTOMATIC.has(item.id) && item.result === 'failed') record(item.id, 'failed', item.reason);
|
|
46
|
+
else if (AUTOMATIC.has(item.id) && item.result === 'fixed') record(item.id, 'fixed');
|
|
47
|
+
else record(item.id, 'skipped');
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
record('doctor', 'failed', '계획 또는 적용 중 예외');
|
|
51
|
+
}
|
|
52
|
+
if (result.later.length) result.lines.push(LATER_LINE);
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
@@ -2,6 +2,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
2
2
|
import { isHeadlessEnv } from '../acp/codex-auth.js';
|
|
3
3
|
import { runNexusShow, type NexusShowResult } from '../cli/nexus-show.js';
|
|
4
4
|
import { debug } from '../debug/log.js';
|
|
5
|
+
import { browserUnavailableReason } from '../oauth/browser-availability.js';
|
|
5
6
|
import type { UserConfig } from '../user-config.js';
|
|
6
7
|
|
|
7
8
|
export interface WebFirstSetupDeps {
|
|
@@ -13,6 +14,8 @@ export interface WebFirstSetupDeps {
|
|
|
13
14
|
renderQr?: (link: string) => string | undefined;
|
|
14
15
|
openBrowser?: (link: string) => boolean;
|
|
15
16
|
isHeadless?: () => boolean;
|
|
17
|
+
browserEnv?: NodeJS.ProcessEnv;
|
|
18
|
+
browserPlatform?: NodeJS.Platform;
|
|
16
19
|
print?: (line: string) => void;
|
|
17
20
|
}
|
|
18
21
|
|
|
@@ -70,7 +73,11 @@ export async function runWebFirstSetup(deps: WebFirstSetupDeps): Promise<'link-s
|
|
|
70
73
|
}
|
|
71
74
|
print('터미널에서 하려면: `elanous setup --terminal`');
|
|
72
75
|
let browserOpened = false;
|
|
73
|
-
|
|
76
|
+
const why = browserUnavailableReason(deps.browserEnv ?? process.env, deps.browserPlatform ?? process.platform);
|
|
77
|
+
if (why) {
|
|
78
|
+
print(why);
|
|
79
|
+
debug.log('browser.open', 'skipped', { reason: why.includes('ssh') ? 'ssh' : 'no-display' });
|
|
80
|
+
} else if (!(deps.isHeadless ?? (() => isHeadlessEnv(deps.browserEnv ?? process.env)))()) {
|
|
74
81
|
try {
|
|
75
82
|
browserOpened = (deps.openBrowser ?? ((url: string) => {
|
|
76
83
|
const command = process.platform === 'darwin' ? 'open' : process.platform === 'linux' || process.platform === 'freebsd' || process.platform === 'openbsd' ? 'xdg-open' : undefined;
|
package/src/onboarding.ts
CHANGED
|
@@ -34,7 +34,7 @@ import { stdin as input, stdout as output } from 'node:process';
|
|
|
34
34
|
import {
|
|
35
35
|
type UserConfig, type LLMProviderName, type SkillSetName,
|
|
36
36
|
saveUserConfig, markOnboardingComplete, buildUserConfig,
|
|
37
|
-
skillSetDir, SKILL_SET_NAMES, userConfigPath, urlRoutingDefaults, devRequestRoutingDefaults,
|
|
37
|
+
skillSetDir, SKILL_SET_NAMES, userConfigPath, urlRoutingDefaults, devRequestRoutingDefaults, defaultSkillDirs, resolveSkillSources,
|
|
38
38
|
} from './user-config.js';
|
|
39
39
|
import {
|
|
40
40
|
GROK_MODEL, OPENAI_MODEL, ANTHROPIC_MODEL, LOCAL_LLM_MODEL, GEMINI_MODEL,
|
|
@@ -45,6 +45,7 @@ import { suggestSetupModel } from './model-tier/index.js';
|
|
|
45
45
|
import { loginWithCodex, CODEX_DEVICE_LOGIN_URL } from './oauth/codex.js';
|
|
46
46
|
import { loginWithCodexBrowser } from './oauth/codex-browser-login.js';
|
|
47
47
|
import { loadTokens } from './oauth/store.js';
|
|
48
|
+
import { browserUnavailableReason } from './oauth/browser-availability.js';
|
|
48
49
|
import { TelegramBot } from './telegram.js';
|
|
49
50
|
import { DiscordBot } from './discord.js';
|
|
50
51
|
import { pickCodexAuthMode, pickCodexModel } from './codex/setup.js';
|
|
@@ -61,6 +62,7 @@ import { stepTransition } from './onboarding/transition.js';
|
|
|
61
62
|
import { unattendedSetupHint } from './onboarding/entry-hints.js';
|
|
62
63
|
import { continueHereLines } from './onboarding/continue-here.js';
|
|
63
64
|
import type { NexusShowResult } from './cli/nexus-show.js';
|
|
65
|
+
import type { TailscaleProbe } from './nexus/onboarding/tailscale-probe.js';
|
|
64
66
|
import type { IssuedTempToken } from './auth/temp-tokens.js';
|
|
65
67
|
import {
|
|
66
68
|
askValidated,
|
|
@@ -385,7 +387,11 @@ function assistCodexDeviceLogin(
|
|
|
385
387
|
const platform = deps.authPlatform ?? process.platform;
|
|
386
388
|
const execute = deps.runAuthCommand ?? runAuthCommand;
|
|
387
389
|
const local = !(deps.isHeadless ?? isHeadlessEnv)(env);
|
|
388
|
-
|
|
390
|
+
const unavailable = browserUnavailableReason(env, platform);
|
|
391
|
+
if (unavailable) {
|
|
392
|
+
io.print(` ${unavailable}`);
|
|
393
|
+
debug.log('browser.open', 'skipped', { reason: unavailable.includes('ssh') ? 'ssh' : 'no-display' });
|
|
394
|
+
} else if (local) {
|
|
389
395
|
try {
|
|
390
396
|
if (platform === 'darwin') execute('open', [url]);
|
|
391
397
|
else if (platform === 'linux' || platform === 'freebsd' || platform === 'openbsd') execute('xdg-open', [url]);
|
|
@@ -582,7 +588,12 @@ async function askLLM(
|
|
|
582
588
|
if (mode === 'oauth') {
|
|
583
589
|
const env = grokDeps.codexEnv ?? process.env;
|
|
584
590
|
let signedIn = false;
|
|
585
|
-
|
|
591
|
+
const why = browserUnavailableReason(env, grokDeps.authPlatform ?? process.platform);
|
|
592
|
+
if (why) {
|
|
593
|
+
io.print(` ${why}`);
|
|
594
|
+
debug.log('browser.open', 'skipped', { reason: why.includes('ssh') ? 'ssh' : 'no-display' });
|
|
595
|
+
}
|
|
596
|
+
if (!why && !(grokDeps.isHeadless ?? isHeadlessEnv)(env)) {
|
|
586
597
|
// Full-screen IO retains print() messages across redraws. Keep this
|
|
587
598
|
// waiting hint transient so it cannot reappear on the device-code screen.
|
|
588
599
|
if (io.showStep) output.write(' 브라우저에서 로그인을 마치세요 — 끝나면 자동으로 이어집니다.\n');
|
|
@@ -600,23 +611,23 @@ async function askLLM(
|
|
|
600
611
|
} catch { /* device code remains available */ }
|
|
601
612
|
}
|
|
602
613
|
}
|
|
603
|
-
if (signedIn) io.print(' →
|
|
614
|
+
if (signedIn) io.print(' → ChatGPT/Codex 로그인을 씁니다.');
|
|
604
615
|
else io.print(' 브라우저 로그인이 안 됐습니다 — 코드로 이어갑니다');
|
|
605
616
|
}
|
|
606
617
|
if (!signedIn) {
|
|
607
|
-
io.print('
|
|
618
|
+
io.print(' 코드로 로그인합니다…');
|
|
608
619
|
try {
|
|
609
620
|
await (grokDeps.loginWithCodex ?? loginWithCodex)({
|
|
610
621
|
onProgress: (p) => {
|
|
611
622
|
if (p.type === 'user_code') {
|
|
612
623
|
io.print('');
|
|
613
|
-
io.print(` 1)
|
|
614
|
-
io.print(` 2)
|
|
624
|
+
io.print(` 1) 아무 브라우저에서 열기: ${p.loginUrl}`);
|
|
625
|
+
io.print(` 2) 코드 입력: ${p.userCode}`);
|
|
615
626
|
io.print('');
|
|
616
627
|
assistCodexDeviceLogin(io, p.loginUrl ?? CODEX_DEVICE_LOGIN_URL, p.userCode ?? '', grokDeps);
|
|
617
|
-
io.print('
|
|
628
|
+
io.print(' 로그인을 기다리는 중…');
|
|
618
629
|
}
|
|
619
|
-
if (p.type === 'saved') io.print('
|
|
630
|
+
if (p.type === 'saved') io.print(' 로그인했습니다. 저장했습니다.');
|
|
620
631
|
},
|
|
621
632
|
});
|
|
622
633
|
} catch (err: any) {
|
|
@@ -1316,6 +1327,7 @@ export interface RunWizardOpts {
|
|
|
1316
1327
|
/** Post-save connections; injection keeps wizard tests off the real daemon/token store. */
|
|
1317
1328
|
continueHereDeps?: {
|
|
1318
1329
|
showNexus?: () => Promise<Pick<NexusShowResult, 'urls' | 'status' | 'instance'>>;
|
|
1330
|
+
probeTailscale?: () => Promise<TailscaleProbe>;
|
|
1319
1331
|
issueToken?: () => Pick<IssuedTempToken, 'token'>;
|
|
1320
1332
|
renderQr?: (link: string) => string | undefined;
|
|
1321
1333
|
};
|
|
@@ -1531,9 +1543,28 @@ export async function runOnboarding(opts: RunWizardOpts = {}): Promise<UserConfi
|
|
|
1531
1543
|
io.print('');
|
|
1532
1544
|
io.print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
1533
1545
|
io.print(` ${m.setupComplete}`);
|
|
1546
|
+
// ER1 — first-run consent notice (default on · 대표 10-01): what is sent and how to turn it off.
|
|
1547
|
+
io.print(' 오류가 나면 운영팀에 자동으로 알립니다(오류 내용·버전·무작위 설치 ID — 이름·메일 없이). 끄기: elanous config set errorReports.enabled false');
|
|
1534
1548
|
io.print(` Provider : ${marked.llm.provider}${marked.llm.model ? ` (${marked.llm.model})` : ''}`);
|
|
1535
|
-
|
|
1536
|
-
|
|
1549
|
+
// EN5 — show what the loader will actually read (resolveSkillSources), not the raw preset fields.
|
|
1550
|
+
const skillSources = resolveSkillSources(marked).filter((source) => source.enabled);
|
|
1551
|
+
io.print(` Skills : ${marked.skills.activeSet} — ${skillSources.length} dir(s)`);
|
|
1552
|
+
for (const source of skillSources) io.print(` ${source.path}${source.kind === 'preset' ? '' : ` (${source.id})`}`);
|
|
1553
|
+
// EN9 — skills the person already has in another agent: one line, one command (registers in place, no copy).
|
|
1554
|
+
try {
|
|
1555
|
+
const { CONNECT_AGENTS, planConnect } = await import('./skills/connect.js');
|
|
1556
|
+
const active = defaultSkillDirs(marked);
|
|
1557
|
+
for (const agent of CONNECT_AGENTS) {
|
|
1558
|
+
const plan = planConnect(agent, active);
|
|
1559
|
+
if (plan.state === 'new') io.print(` 쓰던 스킬 ${plan.skills.length}개 찾음(${agent}) — 가져오려면: elanous connect ${agent}`);
|
|
1560
|
+
}
|
|
1561
|
+
} catch { /* advisory */ }
|
|
1562
|
+
// EN13 — a bot the person already runs elsewhere: one line, one command (values never shown).
|
|
1563
|
+
try {
|
|
1564
|
+
const { channelImportHint } = await import('./channel-import/run.js');
|
|
1565
|
+
const hint = channelImportHint();
|
|
1566
|
+
if (hint) io.print(` ${hint}`);
|
|
1567
|
+
} catch { /* advisory */ }
|
|
1537
1568
|
io.print(` Obsidian : ${marked.obsidian.vault}`);
|
|
1538
1569
|
io.print(` Telegram : ${marked.telegram.enabled ? 'enabled' : 'disabled'}`);
|
|
1539
1570
|
io.print(` Discord : ${marked.discord.enabled ? 'enabled' : 'disabled'}`);
|
|
@@ -1571,7 +1602,30 @@ export async function runOnboarding(opts: RunWizardOpts = {}): Promise<UserConfi
|
|
|
1571
1602
|
}
|
|
1572
1603
|
} catch { /* No token/link on failure; show the manual command instead. */ }
|
|
1573
1604
|
}
|
|
1574
|
-
|
|
1605
|
+
let tailscale: { host?: string } | undefined;
|
|
1606
|
+
let installed = false;
|
|
1607
|
+
let alive = false;
|
|
1608
|
+
let probeTimer: ReturnType<typeof setTimeout> | undefined;
|
|
1609
|
+
try {
|
|
1610
|
+
const probe = opts.continueHereDeps?.probeTailscale ?? (async () => {
|
|
1611
|
+
const { probeTailscale } = await import('./nexus/onboarding/tailscale-probe.js');
|
|
1612
|
+
return probeTailscale({ timeoutMs: 2000 });
|
|
1613
|
+
});
|
|
1614
|
+
const status = await Promise.race([
|
|
1615
|
+
probe(),
|
|
1616
|
+
new Promise<undefined>(resolve => { probeTimer = setTimeout(() => resolve(undefined), 2000); }),
|
|
1617
|
+
]);
|
|
1618
|
+
if (status) {
|
|
1619
|
+
installed = status.installed;
|
|
1620
|
+
alive = status.alive;
|
|
1621
|
+
// A bare hostname has no HTTPS certificate — offer the command without an address.
|
|
1622
|
+
if (installed && alive) tailscale = status.magicDnsHost ? { host: status.magicDnsHost } : {};
|
|
1623
|
+
}
|
|
1624
|
+
} catch { /* Tailscale discovery is advisory. */ }
|
|
1625
|
+
finally { if (probeTimer) clearTimeout(probeTimer); }
|
|
1626
|
+
try { debug.log('onboarding.tailscale', 'probed', { installed, alive, offered: !!tailscale }); }
|
|
1627
|
+
catch { /* Logging must not interrupt setup. */ }
|
|
1628
|
+
const directions = continueHereLines({ daemonRunning, pwaUrl, phoneLink, telegramEnabled: marked.telegram.enabled, interactive, tailscale });
|
|
1575
1629
|
if (io.nonInteractive) {
|
|
1576
1630
|
for (const line of directions) process.stdout.write(`${line}\n`);
|
|
1577
1631
|
} else {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// PS1 — 페르소나 프리셋(MK 문면 · persona-presets/<id>.yaml)을 «내 페르소나»로 만든다.
|
|
2
|
+
// 용어: «페르소나»(대표 10-01) — 사용자가 고르고 꾸미는 AI 동료. ⚠️ `SessionSurfaceId`(이름만 Surface 인 캐릭터 축) ·
|
|
3
|
+
// `modelTier.persona`(모델 등급 축)와는 «다른 것»이다 — 여기서 만드는 것은 기존 페르소나 저장소(`resolveStatePersonaDir()`,
|
|
4
|
+
// Discord·PWA 가 이미 읽는 `<id>.yaml`)의 한 파일이다. 프리셋 원본은 고치지 않는다.
|
|
5
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { parse, stringify } from 'yaml';
|
|
8
|
+
|
|
9
|
+
export interface PersonaPreset {
|
|
10
|
+
personaId: string;
|
|
11
|
+
displayName: string;
|
|
12
|
+
names: string[];
|
|
13
|
+
role: string;
|
|
14
|
+
title?: string | null;
|
|
15
|
+
oneLine: string;
|
|
16
|
+
voice: { rule: string; examples: string[] };
|
|
17
|
+
tasks: Array<{ when: string; what: string }>;
|
|
18
|
+
tools: Array<{ name: string; from: string; optional?: boolean }>;
|
|
19
|
+
firstQuestions: string[];
|
|
20
|
+
askBefore?: string[];
|
|
21
|
+
doesNot: string[];
|
|
22
|
+
forWhom: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface PresetCommon { loop_start?: string; always_ask?: string[]; memory?: string; title?: string }
|
|
26
|
+
|
|
27
|
+
/** `persona-presets/` shipped with this package (src/persona → ../../persona-presets). */
|
|
28
|
+
export function presetsDir(): string {
|
|
29
|
+
return join(import.meta.dir, '..', '..', 'persona-presets');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function loadPresetIndex(dir = presetsDir()): { order: string[]; common: PresetCommon } {
|
|
33
|
+
const index = parse(readFileSync(join(dir, '_index.yaml'), 'utf8')) as { presets?: string[]; common?: PresetCommon };
|
|
34
|
+
return { order: index.presets ?? [], common: index.common ?? {} };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Presets in the index order; files not in the index come last (sorted). */
|
|
38
|
+
export function loadPresets(dir = presetsDir()): PersonaPreset[] {
|
|
39
|
+
if (!existsSync(dir)) return [];
|
|
40
|
+
const { order } = loadPresetIndex(dir);
|
|
41
|
+
const files = readdirSync(dir).filter((f) => f.endsWith('.yaml') && !f.startsWith('_')).map((f) => f.slice(0, -5));
|
|
42
|
+
const ids = [...order.filter((id) => files.includes(id)), ...files.filter((id) => !order.includes(id)).sort()];
|
|
43
|
+
return ids.map((id) => parse(readFileSync(join(dir, `${id}.yaml`), 'utf8')) as PersonaPreset);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Find by id or by any of its names (case-insensitive). */
|
|
47
|
+
export function findPreset(presets: readonly PersonaPreset[], query: string): PersonaPreset | undefined {
|
|
48
|
+
const q = query.trim().toLowerCase();
|
|
49
|
+
return presets.find((p) => p.personaId === q) ?? presets.find((p) => p.names.some((n) => n.toLowerCase() === q) || p.role === query.trim());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The persona's working instructions, assembled from the preset (and the shared rules). */
|
|
53
|
+
export function presetSystemPrompt(p: PersonaPreset, common: PresetCommon, name: string): string {
|
|
54
|
+
const lines = [
|
|
55
|
+
`너는 «${name}», ${p.role}다. ${p.oneLine}`,
|
|
56
|
+
'',
|
|
57
|
+
`말투: ${p.voice.rule}`,
|
|
58
|
+
...p.voice.examples.map((e) => ` 예) ${e}`),
|
|
59
|
+
'',
|
|
60
|
+
'맡은 일:',
|
|
61
|
+
...p.tasks.map((t) => `- ${t.when}: ${t.what}`),
|
|
62
|
+
'',
|
|
63
|
+
`처음 만나면 묻는다: ${p.firstQuestions.join(' / ')}`,
|
|
64
|
+
`먼저 확인을 받는다: ${[...(p.askBefore ?? []), ...(common.always_ask ?? [])].join(' · ')}`,
|
|
65
|
+
'하지 않는다:',
|
|
66
|
+
...p.doesNot.map((d) => `- ${d}`),
|
|
67
|
+
];
|
|
68
|
+
if (common.loop_start) lines.push('', `시작 방식: ${common.loop_start}`);
|
|
69
|
+
return lines.join('\n');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** ASCII id for the profile file: from --name when it is latin, else the preset id; `-2`, `-3`… when taken. */
|
|
73
|
+
export function choosePersonaId(preset: PersonaPreset, name: string | undefined, taken: ReadonlySet<string>): string {
|
|
74
|
+
const fromName = name?.toLowerCase().normalize('NFKD').replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
75
|
+
const base = fromName && /^[a-z0-9]/.test(fromName) ? fromName.slice(0, 32) : preset.personaId;
|
|
76
|
+
if (!taken.has(base)) return base;
|
|
77
|
+
for (let i = 2; ; i++) if (!taken.has(`${base}-${i}`)) return `${base}-${i}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** The profile YAML written to the persona store (the existing loader reads it; `preset` records where it came from). */
|
|
81
|
+
export function presetToProfileYaml(p: PersonaPreset, common: PresetCommon, opts: { personaId: string; name?: string; title?: string }): string {
|
|
82
|
+
const name = opts.name?.trim() || p.names[0] || p.displayName;
|
|
83
|
+
const title = opts.title ?? p.title ?? undefined;
|
|
84
|
+
const profile = {
|
|
85
|
+
personaId: opts.personaId,
|
|
86
|
+
displayName: opts.name?.trim() ? `${opts.name.trim()} · ${p.role}` : p.displayName,
|
|
87
|
+
description: p.oneLine,
|
|
88
|
+
systemPrompt: presetSystemPrompt(p, common, name),
|
|
89
|
+
preset: { id: p.personaId, role: p.role, ...(title ? { title } : {}), tools: p.tools.map((t) => t.name), firstQuestions: p.firstQuestions },
|
|
90
|
+
};
|
|
91
|
+
return `# 프리셋 «${p.personaId}» 에서 만든 페르소나 — elanous persona add (원본: persona-presets/${p.personaId}.yaml)\n${stringify(profile, { lineWidth: 0 })}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Short block for `persona show` and `persona list`. */
|
|
95
|
+
export function describePreset(p: PersonaPreset): string[] {
|
|
96
|
+
return [
|
|
97
|
+
`${p.displayName} — ${p.role}${p.title ? ` (${p.title})` : ''}`,
|
|
98
|
+
` ${p.oneLine}`,
|
|
99
|
+
` 누구에게: ${p.forWhom}`,
|
|
100
|
+
` 맡는 일: ${p.tasks.map((t) => `${t.when} ${t.what}`).join(' / ')}`,
|
|
101
|
+
` 쓰는 도구: ${p.tools.map((t) => (t.optional ? `${t.name}(선택 · 따로 설치)` : t.name)).join(', ')}`,
|
|
102
|
+
` 처음 묻는 것: ${p.firstQuestions.join(' / ')}`,
|
|
103
|
+
` 하지 않는 것: ${p.doesNot.join(' / ')}`,
|
|
104
|
+
];
|
|
105
|
+
}
|