@astrale-os/cli 0.6.3-alpha.0 → 0.8.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/astrale.js +339 -242
- package/package.json +2 -3
- package/src/commands/__tests__/admin-instance.test.ts +49 -2
- package/src/commands/__tests__/domain-install-owned.test.ts +122 -0
- package/src/commands/__tests__/help-contract.test.ts +15 -0
- package/src/commands/domain/install.ts +15 -4
- package/src/commands/instance/active.ts +4 -7
- package/src/commands/instance/list.ts +4 -8
- package/src/commands/instance/status.ts +14 -9
- package/src/commands/instance/use.ts +13 -11
- package/src/commands/studio.ts +13 -0
- package/src/commands/view.ts +40 -13
- package/src/kernel/__tests__/client-owned-lookup.test.ts +51 -0
- package/src/kernel/client.ts +47 -20
- package/src/kernel/expand.ts +3 -22
- package/src/lib/__tests__/instance-candidates.test.ts +94 -19
- package/src/lib/__tests__/instance-target.test.ts +9 -1
- package/src/lib/__tests__/view-assets.test.ts +114 -0
- package/src/lib/__tests__/view-port-allocation.test.ts +82 -0
- package/src/lib/admin-instance.ts +27 -2
- package/src/lib/instance-candidates.ts +3 -3
- package/src/lib/instance-target.ts +1 -0
- package/src/lib/instance.ts +0 -11
- package/src/lib/login-flow.ts +4 -41
- package/src/lib/view/assets.ts +64 -0
- package/src/lib/view/port-allocation.ts +19 -0
- package/src/lib/view/server.ts +4 -37
- package/src/setup/__tests__/instance-step.test.ts +239 -0
- package/src/setup/steps/instance.ts +141 -61
- package/studio/client/dist/assets/index-BaqEuIQJ.js +109 -0
- package/studio/client/dist/assets/index-jRdExahh.css +1 -0
- package/studio/client/dist/index.html +2 -2
- package/studio/server/agent/ask.test.ts +74 -0
- package/studio/server/agent/ask.ts +20 -22
- package/studio/server/agent/bridge/client.test.ts +49 -0
- package/studio/server/agent/bridge/client.ts +37 -0
- package/studio/server/agent/bridge/grant.test.ts +62 -0
- package/studio/server/agent/bridge/grant.ts +84 -0
- package/studio/server/agent/bridge/routes.test.ts +151 -0
- package/studio/server/agent/bridge/routes.ts +148 -0
- package/studio/server/agent/{bridge-mcp.ts → bridge/stdio.ts} +27 -21
- package/studio/server/agent/conversation.test.ts +84 -0
- package/studio/server/agent/conversation.ts +110 -0
- package/studio/server/agent/{types.ts → harness/adapter.ts} +63 -12
- package/studio/server/agent/harness/claude/adapter.test.ts +338 -0
- package/studio/server/agent/harness/claude/adapter.ts +71 -0
- package/studio/server/agent/harness/claude/ask.ts +114 -0
- package/studio/server/agent/harness/claude/capabilities.ts +27 -0
- package/studio/server/agent/harness/claude/command.ts +63 -0
- package/studio/server/agent/harness/claude/events.ts +215 -0
- package/studio/server/agent/harness/claude/loadout.ts +132 -0
- package/studio/server/agent/harness/claude/mcp.test.ts +36 -0
- package/studio/server/agent/harness/claude/mcp.ts +41 -0
- package/studio/server/agent/harness/claude/skills.ts +43 -0
- package/studio/server/agent/harness/codex/adapter.test.ts +269 -0
- package/studio/server/agent/harness/codex/adapter.ts +116 -0
- package/studio/server/agent/harness/codex/ask.test.ts +176 -0
- package/studio/server/agent/harness/codex/ask.ts +181 -0
- package/studio/server/agent/harness/codex/command.test.ts +69 -0
- package/studio/server/agent/harness/codex/command.ts +43 -0
- package/studio/server/agent/harness/codex/events.test.ts +94 -0
- package/studio/server/agent/harness/codex/events.ts +132 -0
- package/studio/server/agent/harness/codex/exec.ts +113 -0
- package/studio/server/agent/harness/codex/loadout.ts +63 -0
- package/studio/server/agent/harness/codex/mcp.test.ts +32 -0
- package/studio/server/agent/harness/codex/mcp.ts +27 -0
- package/studio/server/agent/harness/codex/models.test.ts +167 -0
- package/studio/server/agent/harness/codex/models.ts +211 -0
- package/studio/server/agent/harness/codex/skills.ts +33 -0
- package/studio/server/agent/harness/gateway/config.test.ts +98 -0
- package/studio/server/{state/harness-gateway.ts → agent/harness/gateway/config.ts} +30 -8
- package/studio/server/agent/harness/gateway/token.test.ts +133 -0
- package/studio/server/agent/harness/gateway/token.ts +166 -0
- package/studio/server/agent/harness/mock/adapter.ts +190 -0
- package/studio/server/agent/harness/mock/domain-edit.ts +41 -0
- package/studio/server/agent/harness/process.test.ts +46 -0
- package/studio/server/agent/harness/process.ts +104 -0
- package/studio/server/agent/harness/registry.ts +37 -0
- package/studio/server/agent/harness/selection.test.ts +75 -0
- package/studio/server/agent/harness/selection.ts +77 -0
- package/studio/server/agent/harness/skills.test.ts +95 -0
- package/studio/server/agent/harness/skills.ts +100 -0
- package/studio/server/agent/layout.test.ts +95 -0
- package/studio/server/agent/notify.ts +12 -0
- package/studio/server/agent/{schema-map.ts → prompts/anchors.ts} +2 -2
- package/studio/server/agent/prompts/ask.ts +34 -0
- package/studio/server/agent/{prompt.ts → prompts/system.ts} +3 -107
- package/studio/server/agent/prompts/turn.ts +57 -0
- package/studio/server/agent/routes.test.ts +176 -0
- package/studio/server/agent/routes.ts +214 -0
- package/studio/server/agent/run/completion.ts +193 -0
- package/studio/server/agent/run/coordinator.test.ts +337 -0
- package/studio/server/agent/run/coordinator.ts +104 -0
- package/studio/server/agent/run/live-state.ts +73 -0
- package/studio/server/agent/run/preparation.ts +162 -0
- package/studio/server/agent/run/transcript.test.ts +47 -0
- package/studio/server/agent/run/transcript.ts +30 -0
- package/studio/server/agent/run/usage.test.ts +46 -0
- package/studio/server/{state → agent/run}/usage.ts +4 -4
- package/studio/server/agent/stream.ts +37 -0
- package/studio/server/agent/{session-id.ts → telemetry.ts} +1 -1
- package/studio/server/api-agent-loadout.test.ts +69 -0
- package/studio/server/api.ts +3 -155
- package/studio/server/index.ts +6 -5
- package/studio/server/state/comments.test.ts +29 -0
- package/studio/server/state/comments.ts +3 -3
- package/studio/server/state/settings.test.ts +78 -0
- package/studio/server/state/settings.ts +28 -1
- package/studio/shared/agent-effort.test.ts +12 -0
- package/studio/shared/agent-effort.ts +15 -0
- package/studio/shared/agent-models.test.ts +9 -0
- package/studio/shared/agent-models.ts +14 -0
- package/studio/shared/settings-values.test.ts +18 -0
- package/studio/shared/settings-values.ts +20 -0
- package/studio/shared/types.ts +58 -7
- package/src/connect-core.test.ts +0 -42
- package/src/connect-core.ts +0 -53
- package/studio/client/dist/assets/index-CyN5G8IA.js +0 -109
- package/studio/client/dist/assets/index-DKKMHBBC.css +0 -1
- package/studio/server/agent/bridge.ts +0 -188
- package/studio/server/agent/claude.ts +0 -678
- package/studio/server/agent/mock.ts +0 -186
- package/studio/server/agent/registry.ts +0 -29
- package/studio/server/agent/runner.ts +0 -487
- package/studio/server/state/harness-token.ts +0 -0
|
@@ -1,30 +1,35 @@
|
|
|
1
1
|
import chalk from 'chalk'
|
|
2
2
|
|
|
3
|
+
import type { OwnedInstanceInfo } from '../../lib/admin-instance'
|
|
3
4
|
import type { SetupContext, SetupStep } from '../types'
|
|
4
5
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
6
|
+
import { AstraleError } from '../../errors'
|
|
7
|
+
import { listOwnedInstances } from '../../kernel/client'
|
|
7
8
|
import { normalizeInstanceKernelUrl, setActive, upsertManagedBookmark } from '../../lib/instance'
|
|
8
9
|
import { readLocalStatus } from '../../lib/local-status'
|
|
9
10
|
import { log, withSpinner } from '../../lib/log'
|
|
10
11
|
import { renderInstanceHero } from '../../lib/panel'
|
|
11
12
|
import { confirmDefaultYes, promptText, selectFrom } from '../../lib/prompt'
|
|
12
|
-
import { provisionInstance } from '../../lib/provision-instance'
|
|
13
|
+
import { provisionInstance, type ProvisionResult } from '../../lib/provision-instance'
|
|
13
14
|
import { guiOrigin, slugError } from '../util'
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
16
|
+
export type InstanceSetupDependencies = {
|
|
17
|
+
fetchOwned: (ctx: SetupContext) => Promise<OwnedInstanceInfo[]>
|
|
18
|
+
adopt: (info: OwnedInstanceInfo) => Promise<void>
|
|
19
|
+
selectReady: (instances: OwnedInstanceInfo[]) => Promise<OwnedInstanceInfo | null>
|
|
20
|
+
confirmCreate: () => Promise<boolean>
|
|
21
|
+
promptSlug: () => Promise<string | undefined>
|
|
22
|
+
provision: (slug: string) => Promise<ProvisionResult>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type OwnedInstanceAdoptionDependencies = {
|
|
26
|
+
upsert: (
|
|
27
|
+
key: string,
|
|
28
|
+
slug: string,
|
|
29
|
+
url: string,
|
|
30
|
+
organizationId?: string,
|
|
31
|
+
) => Promise<{ repointedFrom?: string }>
|
|
32
|
+
activate: (slug: string) => Promise<unknown>
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
/** Print the click-inviting hero for a freshly-active instance. */
|
|
@@ -34,9 +39,15 @@ function hero(slug: string, kernelUrl: string): void {
|
|
|
34
39
|
console.log('')
|
|
35
40
|
}
|
|
36
41
|
|
|
37
|
-
async function
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
export async function adoptOwnedInstance(
|
|
43
|
+
info: OwnedInstanceInfo,
|
|
44
|
+
deps: OwnedInstanceAdoptionDependencies = {
|
|
45
|
+
upsert: upsertManagedBookmark,
|
|
46
|
+
activate: setActive,
|
|
47
|
+
},
|
|
48
|
+
): Promise<void> {
|
|
49
|
+
const { repointedFrom } = await deps.upsert(info.slug, info.slug, info.url, info.organizationId)
|
|
50
|
+
await deps.activate(info.slug)
|
|
40
51
|
if (repointedFrom) {
|
|
41
52
|
log.warn(
|
|
42
53
|
`Bookmark "${info.slug}" repointed: ${repointedFrom} → ${normalizeInstanceKernelUrl(info.url)}`,
|
|
@@ -46,10 +57,118 @@ async function adopt(info: InstanceInfo): Promise<void> {
|
|
|
46
57
|
hero(info.slug, info.url)
|
|
47
58
|
}
|
|
48
59
|
|
|
60
|
+
function defaultDependencies(ctx: SetupContext): InstanceSetupDependencies {
|
|
61
|
+
return {
|
|
62
|
+
fetchOwned: (setupCtx) =>
|
|
63
|
+
withSpinner('Checking for existing instances', !setupCtx.machine, () =>
|
|
64
|
+
listOwnedInstances(setupCtx.opts),
|
|
65
|
+
),
|
|
66
|
+
adopt: adoptOwnedInstance,
|
|
67
|
+
selectReady: (instances) =>
|
|
68
|
+
selectFrom(
|
|
69
|
+
'No active instance. Pick one:',
|
|
70
|
+
instances.map((info) => ({
|
|
71
|
+
label: `${info.slug} ${chalk.dim(guiOrigin(info.url))}`,
|
|
72
|
+
value: info,
|
|
73
|
+
})),
|
|
74
|
+
),
|
|
75
|
+
confirmCreate: () => confirmDefaultYes('No instances yet. Provision your first one now?'),
|
|
76
|
+
promptSlug: () =>
|
|
77
|
+
ctx.slug
|
|
78
|
+
? Promise.resolve(ctx.slug)
|
|
79
|
+
: promptText('Pick a slug for your instance', { validate: slugError }),
|
|
80
|
+
provision: (slug) => provisionInstance(slug, ctx.opts),
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Reconcile owner-scoped admin instances when no local active bookmark exists.
|
|
86
|
+
* A failed ownership lookup is never treated as an empty account: setup stops
|
|
87
|
+
* before creation so a transient auth/network failure cannot create a duplicate.
|
|
88
|
+
*/
|
|
89
|
+
export async function ensureOwnedInstance(
|
|
90
|
+
ctx: SetupContext,
|
|
91
|
+
deps: InstanceSetupDependencies = defaultDependencies(ctx),
|
|
92
|
+
): Promise<'fixed' | 'skipped'> {
|
|
93
|
+
let owned: OwnedInstanceInfo[]
|
|
94
|
+
try {
|
|
95
|
+
owned = await deps.fetchOwned(ctx)
|
|
96
|
+
} catch (cause) {
|
|
97
|
+
const detail = cause instanceof Error ? cause.message : String(cause)
|
|
98
|
+
throw new AstraleError(
|
|
99
|
+
'INSTANCE_DISCOVERY_FAILED',
|
|
100
|
+
`Could not check your Astrale instances: ${detail}`,
|
|
101
|
+
'No instance was created. Check `astrale admin status`, then rerun `astrale setup`.',
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const ready = owned.filter((info) => info.state === 'ready')
|
|
106
|
+
if (ready.length === 1) {
|
|
107
|
+
await deps.adopt(ready[0]!)
|
|
108
|
+
return 'fixed'
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (ready.length > 1) {
|
|
112
|
+
const choice = await deps.selectReady(ready)
|
|
113
|
+
if (choice === null) {
|
|
114
|
+
log.dim(' Skipped — no active instance set.')
|
|
115
|
+
return 'skipped'
|
|
116
|
+
}
|
|
117
|
+
await deps.adopt(choice)
|
|
118
|
+
return 'fixed'
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (owned.length > 0) {
|
|
122
|
+
reportNotReady(owned)
|
|
123
|
+
return 'skipped'
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!(await deps.confirmCreate())) {
|
|
127
|
+
log.dim(' Skipped — create one later: astrale instance create <slug>')
|
|
128
|
+
return 'skipped'
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const slug = await deps.promptSlug()
|
|
132
|
+
if (!slug) {
|
|
133
|
+
log.dim(' Skipped — no slug given.')
|
|
134
|
+
return 'skipped'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const { created, selectionError } = await deps.provision(slug)
|
|
138
|
+
if (selectionError) {
|
|
139
|
+
const detail = selectionError instanceof Error ? selectionError.message : String(selectionError)
|
|
140
|
+
throw new AstraleError(
|
|
141
|
+
'INSTANCE_SELECTION_FAILED',
|
|
142
|
+
`Instance "${slug}" was provisioned, but the CLI could not select it: ${detail}`,
|
|
143
|
+
`Fix local CLI storage, then run \`astrale instance use ${slug}\`.`,
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
hero(slug, created.url)
|
|
147
|
+
return 'fixed'
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function reportNotReady(instances: OwnedInstanceInfo[]): void {
|
|
151
|
+
log.warn(
|
|
152
|
+
`${instances.length === 1 ? 'Your instance is' : 'Your instances are'} not ready; setup will not create another.`,
|
|
153
|
+
)
|
|
154
|
+
for (const info of instances) {
|
|
155
|
+
const phase = info.phase && info.phase !== info.state ? ` (${info.phase})` : ''
|
|
156
|
+
log.dim(` ${info.slug}: ${info.state}${phase} · astrale instance status ${info.slug}`)
|
|
157
|
+
if (info.error) log.dim(` ${info.error}`)
|
|
158
|
+
}
|
|
159
|
+
if (instances.some((info) => info.state === 'failed')) {
|
|
160
|
+
log.dim(
|
|
161
|
+
' Inspect the failure, then deliberately delete/recreate it with `astrale instance` commands.',
|
|
162
|
+
)
|
|
163
|
+
} else {
|
|
164
|
+
log.dim(' Wait for provisioning to finish, then rerun `astrale setup`.')
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
49
168
|
/**
|
|
50
169
|
* Step 3 — an active instance, the heart of the flow. If the user already has
|
|
51
|
-
*
|
|
52
|
-
*
|
|
170
|
+
* owned instances, adopt the sole ready one or ask among several; only a
|
|
171
|
+
* confirmed empty owner list may fall through to first-instance provisioning.
|
|
53
172
|
*/
|
|
54
173
|
export const instanceStep: SetupStep = {
|
|
55
174
|
id: 'instance',
|
|
@@ -75,45 +194,6 @@ export const instanceStep: SetupStep = {
|
|
|
75
194
|
return 'unchanged'
|
|
76
195
|
}
|
|
77
196
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// Pick an existing instance or fall through to creating a new one.
|
|
81
|
-
if (managed.length > 0) {
|
|
82
|
-
const choice = await selectFrom<InstanceInfo | 'create'>(
|
|
83
|
-
'No active instance. Pick one or create a new instance:',
|
|
84
|
-
[
|
|
85
|
-
...managed.map((info) => ({
|
|
86
|
-
label: `${info.slug} ${chalk.dim(guiOrigin(info.url))}`,
|
|
87
|
-
value: info as InstanceInfo | 'create',
|
|
88
|
-
})),
|
|
89
|
-
{ label: chalk.cyan('➕ Create a new instance'), value: 'create' as const },
|
|
90
|
-
],
|
|
91
|
-
)
|
|
92
|
-
if (choice === null) {
|
|
93
|
-
log.dim(' Skipped — no active instance set.')
|
|
94
|
-
return 'skipped'
|
|
95
|
-
}
|
|
96
|
-
if (choice !== 'create') {
|
|
97
|
-
await adopt(choice)
|
|
98
|
-
return 'fixed'
|
|
99
|
-
}
|
|
100
|
-
} else {
|
|
101
|
-
const yes = await confirmDefaultYes('No instances yet. Provision your first one now?')
|
|
102
|
-
if (!yes) {
|
|
103
|
-
log.dim(' Skipped — create one later: astrale instance create <slug>')
|
|
104
|
-
return 'skipped'
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const slug =
|
|
109
|
-
ctx.slug ?? (await promptText('Pick a slug for your instance', { validate: slugError }))
|
|
110
|
-
if (!slug) {
|
|
111
|
-
log.dim(' Skipped — no slug given.')
|
|
112
|
-
return 'skipped'
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const { created } = await provisionInstance(slug, ctx.opts)
|
|
116
|
-
hero(slug, created.url)
|
|
117
|
-
return 'fixed'
|
|
197
|
+
return ensureOwnedInstance(ctx)
|
|
118
198
|
},
|
|
119
199
|
}
|