@kal-elsam/kairo-runtime 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -2
- package/package.json +1 -1
- package/src/cli.js +21 -6
- package/src/global/brand/index.js +10 -0
- package/src/global/dashboard-guidance.js +66 -0
- package/src/global/initial-experience.js +34 -0
- package/src/global/ink/orchestrator-app.js +18 -2
- package/src/global/ink/run-orchestrator-ink.js +2 -0
- package/src/global/ink/run-setup-ink.js +2 -0
- package/src/global/ink/setup-app.js +8 -4
- package/src/global/ink/setup-state.js +24 -2
- package/src/global/orchestrator.js +40 -5
- package/src/global/setup.js +13 -5
package/README.md
CHANGED
|
@@ -24,13 +24,24 @@ commands) without depending on Pi as a runtime or adding a Pi adapter.
|
|
|
24
24
|
|
|
25
25
|
## Quick start
|
|
26
26
|
|
|
27
|
-
Recommended entry — run Kairo Runtime in your terminal
|
|
27
|
+
Recommended entry — run Kairo Runtime in your terminal:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
30
|
npx @kal-elsam/kairo-runtime
|
|
31
|
+
# or, after a global install:
|
|
32
|
+
kairo
|
|
31
33
|
```
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
**First run** (no `~/.harness/state.json`): interactive onboarding → safe diagnosis →
|
|
36
|
+
setup with confirmation → operations dashboard.
|
|
37
|
+
|
|
38
|
+
**Later runs** (state present): operations dashboard with a stable purpose line and a
|
|
39
|
+
contextual next step (configure, enable intelligence, launch a run, or review problems).
|
|
40
|
+
|
|
41
|
+
Explicit commands and setup flags keep their current behavior (`kairo setup`,
|
|
42
|
+
`kairo --dry-run`, `kairo shell`, non-TTY scripts, etc.).
|
|
43
|
+
|
|
44
|
+
Preview setup without writing anything:
|
|
34
45
|
|
|
35
46
|
```bash
|
|
36
47
|
npx @kal-elsam/kairo-runtime --dry-run
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kal-elsam/kairo-runtime",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Kairo Runtime — local agent operating system for Codex, Cursor, Claude, Gemini, Copilot, Engram, and Graphify.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://github.com/Kal-elSam/harness#readme",
|
package/src/cli.js
CHANGED
|
@@ -42,6 +42,11 @@ import {
|
|
|
42
42
|
resolveSuggestedInvocation
|
|
43
43
|
} from "./global/brand/cli.js";
|
|
44
44
|
import { BRAND } from "./global/brand/index.js";
|
|
45
|
+
import {
|
|
46
|
+
INITIAL_EXPERIENCE,
|
|
47
|
+
hasConfiguredGlobalState,
|
|
48
|
+
resolveInitialExperience
|
|
49
|
+
} from "./global/initial-experience.js";
|
|
45
50
|
|
|
46
51
|
export { resolveSuggestedInvocation };
|
|
47
52
|
|
|
@@ -50,7 +55,7 @@ const packageRoot = resolve(__dirname, "..");
|
|
|
50
55
|
const SCOPES = new Set(["agent-global", "workspace"]);
|
|
51
56
|
|
|
52
57
|
export async function runCli(argv) {
|
|
53
|
-
const { command, options } = parseArgs(argv);
|
|
58
|
+
const { command, options, isImplicitCommand } = parseArgs(argv);
|
|
54
59
|
maybeWarnLegacyCli(process.argv, { json: options.json });
|
|
55
60
|
|
|
56
61
|
if (options.help || command === "help") {
|
|
@@ -69,14 +74,22 @@ export async function runCli(argv) {
|
|
|
69
74
|
const invoke = resolveSuggestedInvocation(packageManifest.name);
|
|
70
75
|
|
|
71
76
|
switch (command) {
|
|
72
|
-
case "shell":
|
|
77
|
+
case "shell": {
|
|
78
|
+
const homeDir = resolveHomeDir();
|
|
79
|
+
const resolvedMode = resolveInitialExperience({
|
|
80
|
+
interactive: optionsWithPolicy.interactive,
|
|
81
|
+
isImplicitCommand,
|
|
82
|
+
hasGlobalState: hasConfiguredGlobalState(homeDir)
|
|
83
|
+
});
|
|
73
84
|
await runOrchestratorShell({
|
|
74
85
|
packageRoot,
|
|
75
86
|
packageManifest,
|
|
76
87
|
workspaceRoot: optionsWithPolicy.cwd,
|
|
77
|
-
interactive: optionsWithPolicy.interactive
|
|
88
|
+
interactive: optionsWithPolicy.interactive,
|
|
89
|
+
initialMode: resolvedMode ?? INITIAL_EXPERIENCE.DASHBOARD
|
|
78
90
|
});
|
|
79
91
|
return;
|
|
92
|
+
}
|
|
80
93
|
case "orchestrator":
|
|
81
94
|
await runOrchestratorDiagnostics({
|
|
82
95
|
homeDir: resolveHomeDir(),
|
|
@@ -468,7 +481,7 @@ export function parseArgs(argv) {
|
|
|
468
481
|
options.task = args.join(" ").trim();
|
|
469
482
|
}
|
|
470
483
|
|
|
471
|
-
return { command, options };
|
|
484
|
+
return { command, options, isImplicitCommand: implicitCommand };
|
|
472
485
|
}
|
|
473
486
|
|
|
474
487
|
function parseComponentsAction(args, options) {
|
|
@@ -683,7 +696,8 @@ sections, components, backups, and drift repair under ~/.harness.
|
|
|
683
696
|
Bootstrap: see README.md (curl install.sh or npx ${PACKAGE_NAME}).
|
|
684
697
|
|
|
685
698
|
Usage:
|
|
686
|
-
${cli}
|
|
699
|
+
${cli} First run: onboarding → setup → dashboard (TTY).
|
|
700
|
+
Later: operations dashboard with next-step guidance.
|
|
687
701
|
${cli} --dry-run Setup dry-run (scriptable)
|
|
688
702
|
${cli} --version
|
|
689
703
|
${cli} shell Operations dashboard (TTY)
|
|
@@ -727,7 +741,8 @@ Scopes:
|
|
|
727
741
|
Explicit --scope=workspace only.
|
|
728
742
|
|
|
729
743
|
Commands:
|
|
730
|
-
shell Operations dashboard (TTY). Bare ${cli} opens
|
|
744
|
+
shell Operations dashboard (TTY). Bare ${cli} opens onboarding when ~/.harness/state.json
|
|
745
|
+
is missing, otherwise the dashboard. Explicit ${cli} shell always opens the dashboard.
|
|
731
746
|
run Launch a managed agent run with local audit trail.
|
|
732
747
|
runs List, inspect, or cancel agent runs under ~/.harness/runs/.
|
|
733
748
|
orchestrator Read-only capability registry diagnostics (--json supported).
|
|
@@ -70,6 +70,16 @@ export const WIZARD_COPY = {
|
|
|
70
70
|
coreOnlyLabel: "Core only (no components)"
|
|
71
71
|
};
|
|
72
72
|
|
|
73
|
+
/** First-run framing reused by onboarding → setup. */
|
|
74
|
+
export const ONBOARDING_COPY = {
|
|
75
|
+
welcomeTitle: `Welcome to ${BRAND.displayName}`,
|
|
76
|
+
purpose:
|
|
77
|
+
`${BRAND.displayName} detects, configures, and coordinates the local agents you already use.`,
|
|
78
|
+
safety:
|
|
79
|
+
"Diagnosis is read-only. Nothing is modified until you confirm a plan.",
|
|
80
|
+
continueHint: "Press Enter to diagnose and configure · Esc to exit"
|
|
81
|
+
};
|
|
82
|
+
|
|
73
83
|
export function getAgentLabel(agentId) {
|
|
74
84
|
return AGENT_LABELS[agentId] ?? agentId;
|
|
75
85
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { formatCliCommand } from "./brand/cli.js";
|
|
2
|
+
|
|
3
|
+
export const DASHBOARD_PURPOSE =
|
|
4
|
+
"Detects, configures, and coordinates local AI agents — no changes without confirmation.";
|
|
5
|
+
|
|
6
|
+
export const NEXT_STEP_KINDS = {
|
|
7
|
+
CONFIGURE: "configure",
|
|
8
|
+
ENABLE_INTELLIGENCE: "enable_intelligence",
|
|
9
|
+
LAUNCH: "launch",
|
|
10
|
+
REVIEW: "review"
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function formatDashboardPurpose() {
|
|
14
|
+
return DASHBOARD_PURPOSE;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Contextual next step from existing diagnostics + dashboard snapshot.
|
|
19
|
+
* Priority: configure → review problems → enable intelligence → launch.
|
|
20
|
+
*/
|
|
21
|
+
export function resolveDashboardRecommendation({
|
|
22
|
+
hasGlobalState = false,
|
|
23
|
+
diagnostics = null,
|
|
24
|
+
dashboard = null
|
|
25
|
+
} = {}) {
|
|
26
|
+
const summary = diagnostics?.diagnostics ?? { detected: 0, errors: 0 };
|
|
27
|
+
const intelligence = diagnostics?.intelligence?.summary;
|
|
28
|
+
const launchableCount = (dashboard?.providers ?? []).filter((entry) => entry.launchable).length;
|
|
29
|
+
const hasErrors = (summary.errors ?? 0) > 0;
|
|
30
|
+
const hasProblemRecommendation = (diagnostics?.recommendations ?? []).some((line) =>
|
|
31
|
+
/error|fix|drift|not detected|failed|problem/i.test(line)
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
if (!hasGlobalState || (summary.detected ?? 0) === 0) {
|
|
35
|
+
return {
|
|
36
|
+
kind: NEXT_STEP_KINDS.CONFIGURE,
|
|
37
|
+
message: `Configure the local environment with ${formatCliCommand("setup")}.`
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (hasErrors || hasProblemRecommendation) {
|
|
42
|
+
return {
|
|
43
|
+
kind: NEXT_STEP_KINDS.REVIEW,
|
|
44
|
+
message: "Review diagnostics for problems before launching a run."
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!intelligence?.localAvailable && !intelligence?.cloudAuthenticated) {
|
|
49
|
+
return {
|
|
50
|
+
kind: NEXT_STEP_KINDS.ENABLE_INTELLIGENCE,
|
|
51
|
+
message: "Enable intelligence: start Ollama or set OPENROUTER_API_KEY, then retry."
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (launchableCount > 0) {
|
|
56
|
+
return {
|
|
57
|
+
kind: NEXT_STEP_KINDS.LAUNCH,
|
|
58
|
+
message: "Launch a supervised run from the menu or with kairo run."
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
kind: NEXT_STEP_KINDS.REVIEW,
|
|
64
|
+
message: "Review diagnostics for problems before launching a run."
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { harnessHomePaths } from "./paths.js";
|
|
3
|
+
|
|
4
|
+
export const INITIAL_EXPERIENCE = {
|
|
5
|
+
ONBOARDING: "onboarding",
|
|
6
|
+
DASHBOARD: "dashboard"
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* First-run marker is ~/.harness/state.json only.
|
|
11
|
+
* profile.json does not participate in this decision.
|
|
12
|
+
*/
|
|
13
|
+
export function hasConfiguredGlobalState(homeDir) {
|
|
14
|
+
return existsSync(harnessHomePaths(homeDir).statePath);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Pure resolver for the interactive bare-entry experience.
|
|
19
|
+
* Returns null when CLI should keep existing non-onboarding paths
|
|
20
|
+
* (non-TTY, explicit commands, setup flags already routed elsewhere).
|
|
21
|
+
*/
|
|
22
|
+
export function resolveInitialExperience({
|
|
23
|
+
interactive = false,
|
|
24
|
+
isImplicitCommand = false,
|
|
25
|
+
hasGlobalState = false
|
|
26
|
+
} = {}) {
|
|
27
|
+
if (!interactive || !isImplicitCommand) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return hasGlobalState
|
|
32
|
+
? INITIAL_EXPERIENCE.DASHBOARD
|
|
33
|
+
: INITIAL_EXPERIENCE.ONBOARDING;
|
|
34
|
+
}
|
|
@@ -27,6 +27,10 @@ import {
|
|
|
27
27
|
selectRunFromList,
|
|
28
28
|
shiftMenuIndex
|
|
29
29
|
} from "./orchestrator-state.js";
|
|
30
|
+
import {
|
|
31
|
+
formatDashboardPurpose,
|
|
32
|
+
resolveDashboardRecommendation
|
|
33
|
+
} from "../dashboard-guidance.js";
|
|
30
34
|
|
|
31
35
|
const COLORS = {
|
|
32
36
|
accent: "cyan",
|
|
@@ -42,6 +46,7 @@ export function OrchestratorApp({
|
|
|
42
46
|
packageName,
|
|
43
47
|
packageRoot,
|
|
44
48
|
cliVersion,
|
|
49
|
+
hasGlobalState = false,
|
|
45
50
|
onComplete
|
|
46
51
|
}) {
|
|
47
52
|
const { exit } = useApp();
|
|
@@ -361,13 +366,14 @@ export function OrchestratorApp({
|
|
|
361
366
|
|
|
362
367
|
return React.createElement(Box, { flexDirection: "column" },
|
|
363
368
|
React.createElement(Text, { bold: true, color: COLORS.accent }, `${BRAND.displayName} runtime`),
|
|
364
|
-
React.createElement(Text, { color: COLORS.muted },
|
|
369
|
+
React.createElement(Text, { color: COLORS.muted }, formatDashboardPurpose()),
|
|
365
370
|
statusMessage && React.createElement(Text, { color: COLORS.success }, statusMessage),
|
|
366
371
|
React.createElement(Text, null, ""),
|
|
367
372
|
renderView({
|
|
368
373
|
view,
|
|
369
374
|
dashboard,
|
|
370
375
|
diagnostics,
|
|
376
|
+
hasGlobalState,
|
|
371
377
|
menuIndex,
|
|
372
378
|
listIndex,
|
|
373
379
|
launchStep,
|
|
@@ -387,6 +393,7 @@ function renderView({
|
|
|
387
393
|
view,
|
|
388
394
|
dashboard,
|
|
389
395
|
diagnostics,
|
|
396
|
+
hasGlobalState,
|
|
390
397
|
menuIndex,
|
|
391
398
|
listIndex,
|
|
392
399
|
launchStep,
|
|
@@ -398,8 +405,16 @@ function renderView({
|
|
|
398
405
|
selectedEvents
|
|
399
406
|
}) {
|
|
400
407
|
switch (view) {
|
|
401
|
-
case ORCHESTRATOR_VIEWS.HOME:
|
|
408
|
+
case ORCHESTRATOR_VIEWS.HOME: {
|
|
409
|
+
const nextStep = resolveDashboardRecommendation({
|
|
410
|
+
hasGlobalState,
|
|
411
|
+
diagnostics,
|
|
412
|
+
dashboard
|
|
413
|
+
});
|
|
402
414
|
return React.createElement(Box, { flexDirection: "column" },
|
|
415
|
+
React.createElement(Text, { bold: true }, "Next"),
|
|
416
|
+
React.createElement(Text, { color: COLORS.accent }, nextStep.message),
|
|
417
|
+
React.createElement(Text, null, ""),
|
|
403
418
|
React.createElement(Text, { bold: true }, "Operations"),
|
|
404
419
|
ORCHESTRATOR_MENU.map((item, index) =>
|
|
405
420
|
React.createElement(Text, {
|
|
@@ -413,6 +428,7 @@ function renderView({
|
|
|
413
428
|
formatDashboardSnapshot(dashboard)
|
|
414
429
|
.map((line) => React.createElement(Text, { key: line }, line))
|
|
415
430
|
);
|
|
431
|
+
}
|
|
416
432
|
case ORCHESTRATOR_VIEWS.ACTIVE_RUNS:
|
|
417
433
|
return React.createElement(Box, { flexDirection: "column" },
|
|
418
434
|
React.createElement(Text, { bold: true }, "Active runs"),
|
|
@@ -8,6 +8,7 @@ export async function runOrchestratorInk({
|
|
|
8
8
|
packageRoot,
|
|
9
9
|
packageName,
|
|
10
10
|
cliVersion,
|
|
11
|
+
hasGlobalState = false,
|
|
11
12
|
renderImpl = render
|
|
12
13
|
}) {
|
|
13
14
|
return new Promise((resolve) => {
|
|
@@ -18,6 +19,7 @@ export async function runOrchestratorInk({
|
|
|
18
19
|
packageRoot,
|
|
19
20
|
packageName,
|
|
20
21
|
cliVersion,
|
|
22
|
+
hasGlobalState,
|
|
21
23
|
onComplete: resolve
|
|
22
24
|
})
|
|
23
25
|
);
|
|
@@ -15,6 +15,7 @@ export async function runSetupInk({
|
|
|
15
15
|
packageName,
|
|
16
16
|
cliVersion,
|
|
17
17
|
dryRun = false,
|
|
18
|
+
onboarding = false,
|
|
18
19
|
preflight = true,
|
|
19
20
|
yes = false,
|
|
20
21
|
confirm = false,
|
|
@@ -33,6 +34,7 @@ export async function runSetupInk({
|
|
|
33
34
|
packageName,
|
|
34
35
|
cliVersion,
|
|
35
36
|
dryRun,
|
|
37
|
+
onboarding,
|
|
36
38
|
onComplete: resolve
|
|
37
39
|
})
|
|
38
40
|
);
|
|
@@ -56,8 +56,8 @@ function Footer({ children }) {
|
|
|
56
56
|
return React.createElement(Text, { dimColor: true }, children);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function Splash({ compact }) {
|
|
60
|
-
const lines = formatInkSplashLines({ compact });
|
|
59
|
+
function Splash({ compact, onboarding = false }) {
|
|
60
|
+
const lines = formatInkSplashLines({ compact, onboarding });
|
|
61
61
|
const logoLineCount = compact ? BRAND.compactLogo.length : BRAND.asciiLogo.length;
|
|
62
62
|
|
|
63
63
|
return React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
|
|
@@ -71,7 +71,7 @@ function Splash({ compact }) {
|
|
|
71
71
|
if (line === BRAND.tagline) {
|
|
72
72
|
return React.createElement(Text, { key: `line-${index}`, color: INK_COLORS.muted }, line);
|
|
73
73
|
}
|
|
74
|
-
if (line === BRAND.splashHint) {
|
|
74
|
+
if (line === BRAND.splashHint || line.includes("Esc to exit") || line.includes("Press Enter")) {
|
|
75
75
|
return React.createElement(Text, { key: `line-${index}`, dimColor: true }, line);
|
|
76
76
|
}
|
|
77
77
|
if (line === "") {
|
|
@@ -89,6 +89,7 @@ export function SetupApp({
|
|
|
89
89
|
packageName,
|
|
90
90
|
cliVersion,
|
|
91
91
|
dryRun = false,
|
|
92
|
+
onboarding = false,
|
|
92
93
|
onComplete
|
|
93
94
|
}) {
|
|
94
95
|
const { exit } = useApp();
|
|
@@ -243,7 +244,10 @@ export function SetupApp({
|
|
|
243
244
|
const detectPanel = formatInkDetectPanel({ adapters, detected });
|
|
244
245
|
|
|
245
246
|
return React.createElement(Box, { flexDirection: "column" },
|
|
246
|
-
step === SETUP_STEPS.SPLASH && React.createElement(Splash, {
|
|
247
|
+
step === SETUP_STEPS.SPLASH && React.createElement(Splash, {
|
|
248
|
+
compact: useCompactSplash,
|
|
249
|
+
onboarding
|
|
250
|
+
}),
|
|
247
251
|
step !== SETUP_STEPS.SPLASH && React.createElement(Header),
|
|
248
252
|
step === SETUP_STEPS.DETECT && React.createElement(Panel, { title: WIZARD_COPY.detectTitle },
|
|
249
253
|
detectPanel.split("\n")
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
AGENT_HINTS,
|
|
3
|
+
BRAND,
|
|
4
|
+
ONBOARDING_COPY,
|
|
5
|
+
formatCliCommand,
|
|
6
|
+
getAgentLabel,
|
|
7
|
+
PREFERRED_CLI,
|
|
8
|
+
WIZARD_COPY
|
|
9
|
+
} from "../brand/index.js";
|
|
2
10
|
import { formatAgentMultiselectHint } from "../clack/theme.js";
|
|
3
11
|
|
|
4
12
|
export const SETUP_STEPS = {
|
|
@@ -16,8 +24,22 @@ export function shouldUseCompactSplashLogo(columns) {
|
|
|
16
24
|
return columns < fullWidth + 4;
|
|
17
25
|
}
|
|
18
26
|
|
|
19
|
-
export function formatInkSplashLines({ compact = false } = {}) {
|
|
27
|
+
export function formatInkSplashLines({ compact = false, onboarding = false } = {}) {
|
|
20
28
|
const logo = compact ? BRAND.compactLogo : BRAND.asciiLogo;
|
|
29
|
+
if (onboarding) {
|
|
30
|
+
return [
|
|
31
|
+
...logo,
|
|
32
|
+
"",
|
|
33
|
+
BRAND.name,
|
|
34
|
+
BRAND.tagline,
|
|
35
|
+
"",
|
|
36
|
+
ONBOARDING_COPY.purpose,
|
|
37
|
+
ONBOARDING_COPY.safety,
|
|
38
|
+
"",
|
|
39
|
+
ONBOARDING_COPY.continueHint
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
|
|
21
43
|
return [
|
|
22
44
|
...logo,
|
|
23
45
|
"",
|
|
@@ -5,6 +5,11 @@ import { runOrchestratorInk as defaultRunOrchestratorInk } from "./ink/run-orche
|
|
|
5
5
|
import { formatCliCommand } from "./brand/cli.js";
|
|
6
6
|
import { BRAND } from "./brand/index.js";
|
|
7
7
|
import { buildReadOnlyDiagnostics, shouldExecutePlan } from "./action-planner.js";
|
|
8
|
+
import { runHarnessSetup as defaultRunHarnessSetup } from "./setup.js";
|
|
9
|
+
import {
|
|
10
|
+
INITIAL_EXPERIENCE,
|
|
11
|
+
hasConfiguredGlobalState
|
|
12
|
+
} from "./initial-experience.js";
|
|
8
13
|
|
|
9
14
|
export { canUseOrchestratorShell };
|
|
10
15
|
|
|
@@ -23,7 +28,10 @@ export async function runOrchestratorShell({
|
|
|
23
28
|
packageManifest,
|
|
24
29
|
workspaceRoot,
|
|
25
30
|
interactive = Boolean(input.isTTY && output.isTTY),
|
|
26
|
-
|
|
31
|
+
initialMode = INITIAL_EXPERIENCE.DASHBOARD,
|
|
32
|
+
shellCapable = canUseOrchestratorShell({ interactive }),
|
|
33
|
+
runOrchestratorInkImpl = defaultRunOrchestratorInk,
|
|
34
|
+
runHarnessSetupImpl = defaultRunHarnessSetup
|
|
27
35
|
}) {
|
|
28
36
|
if (!interactive) {
|
|
29
37
|
throw new Error(
|
|
@@ -31,19 +39,44 @@ export async function runOrchestratorShell({
|
|
|
31
39
|
);
|
|
32
40
|
}
|
|
33
41
|
|
|
34
|
-
if (!
|
|
42
|
+
if (!shellCapable) {
|
|
35
43
|
throw new Error(
|
|
36
44
|
`Interactive shell requires a capable TTY. Use ${formatCliCommand("runs list")} or explicit commands.`
|
|
37
45
|
);
|
|
38
46
|
}
|
|
39
47
|
|
|
40
48
|
const homeDir = resolveHomeDir();
|
|
49
|
+
let setupOutcome = null;
|
|
50
|
+
|
|
51
|
+
if (initialMode === INITIAL_EXPERIENCE.ONBOARDING) {
|
|
52
|
+
setupOutcome = await runHarnessSetupImpl({
|
|
53
|
+
packageRoot,
|
|
54
|
+
packageName: packageManifest.name,
|
|
55
|
+
cliVersion: packageManifest.version,
|
|
56
|
+
homeDir,
|
|
57
|
+
workspaceRoot,
|
|
58
|
+
onboarding: true,
|
|
59
|
+
interactive: true
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (setupOutcome?.cancelled) {
|
|
63
|
+
return {
|
|
64
|
+
cancelled: true,
|
|
65
|
+
wrote: false,
|
|
66
|
+
action: null,
|
|
67
|
+
initialMode,
|
|
68
|
+
setup: setupOutcome
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
41
73
|
const outcome = await runOrchestratorInkImpl({
|
|
42
74
|
homeDir,
|
|
43
75
|
workspaceRoot,
|
|
44
76
|
packageRoot,
|
|
45
77
|
packageName: packageManifest.name,
|
|
46
|
-
cliVersion: packageManifest.version
|
|
78
|
+
cliVersion: packageManifest.version,
|
|
79
|
+
hasGlobalState: hasConfiguredGlobalState(homeDir)
|
|
47
80
|
});
|
|
48
81
|
|
|
49
82
|
if (outcome.error) {
|
|
@@ -52,8 +85,10 @@ export async function runOrchestratorShell({
|
|
|
52
85
|
|
|
53
86
|
return {
|
|
54
87
|
cancelled: Boolean(outcome.cancelled),
|
|
55
|
-
wrote:
|
|
56
|
-
action: outcome.action ?? null
|
|
88
|
+
wrote: Boolean(setupOutcome && !setupOutcome.cancelled),
|
|
89
|
+
action: outcome.action ?? null,
|
|
90
|
+
initialMode,
|
|
91
|
+
setup: setupOutcome
|
|
57
92
|
};
|
|
58
93
|
}
|
|
59
94
|
|
package/src/global/setup.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { stdin as input, stdout as output } from "node:process";
|
|
2
|
-
import { BRAND } from "./brand/index.js";
|
|
2
|
+
import { BRAND, ONBOARDING_COPY } from "./brand/index.js";
|
|
3
3
|
import { installGlobalHarness } from "./global-installer.js";
|
|
4
4
|
import {
|
|
5
5
|
assertExplicitApplyConsent,
|
|
@@ -59,6 +59,7 @@ export async function runHarnessSetup({
|
|
|
59
59
|
confirmExplicit = false,
|
|
60
60
|
json = false,
|
|
61
61
|
simple = false,
|
|
62
|
+
onboarding = false,
|
|
62
63
|
interactive = Boolean(input.isTTY && output.isTTY),
|
|
63
64
|
createPrompt = createReadlinePrompt,
|
|
64
65
|
runSetupInkImpl = defaultRunSetupInk,
|
|
@@ -76,7 +77,7 @@ export async function runHarnessSetup({
|
|
|
76
77
|
let usedInk = false;
|
|
77
78
|
|
|
78
79
|
if (!useInk && !useWizard) {
|
|
79
|
-
printSetupIntro({ homeDir });
|
|
80
|
+
printSetupIntro({ homeDir, onboarding });
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
const setupUiArgs = {
|
|
@@ -86,6 +87,7 @@ export async function runHarnessSetup({
|
|
|
86
87
|
packageName,
|
|
87
88
|
cliVersion,
|
|
88
89
|
dryRun,
|
|
90
|
+
onboarding,
|
|
89
91
|
preflight,
|
|
90
92
|
yes,
|
|
91
93
|
confirm,
|
|
@@ -254,11 +256,17 @@ export async function runHarnessSetup({
|
|
|
254
256
|
return { cancelled: false, result, usedWizard, usedInk };
|
|
255
257
|
}
|
|
256
258
|
|
|
257
|
-
function printSetupIntro({ homeDir }) {
|
|
259
|
+
function printSetupIntro({ homeDir, onboarding = false }) {
|
|
258
260
|
const detected = detectInstalledAdapters({ homeDir });
|
|
259
261
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
+
if (onboarding) {
|
|
263
|
+
console.log(ONBOARDING_COPY.welcomeTitle);
|
|
264
|
+
console.log(ONBOARDING_COPY.purpose);
|
|
265
|
+
console.log(ONBOARDING_COPY.safety);
|
|
266
|
+
} else {
|
|
267
|
+
console.log(`${BRAND.displayName} setup — local AI ecosystem configurator`);
|
|
268
|
+
console.log("Configures and coordinates local agents. Does not install the AI apps themselves.");
|
|
269
|
+
}
|
|
262
270
|
console.log("");
|
|
263
271
|
console.log(`Detected agents: ${detected.join(", ") || "none"}`);
|
|
264
272
|
console.log(`Supported agents: ${GLOBAL_AGENT_IDS.join(", ")}`);
|