@astrale-os/cli 1.0.0-beta.2 → 1.0.0-beta.20
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 +16 -1
- package/dist/astrale.js +51105 -58057
- package/dist/public/connect-core.js +2085 -30702
- package/dist/public/keys/index.js +884 -29476
- package/dist/public/paths/index.js +13101 -41452
- package/dist/types/admin/contract.d.ts +26 -0
- package/dist/types/admin/instance/client.d.ts +20 -0
- package/dist/types/admin/instance/index.d.ts +2 -0
- package/dist/types/admin/instance/model.d.ts +6 -10
- package/dist/types/connection/auth.d.ts +3 -0
- package/dist/types/connection/call.d.ts +3 -0
- package/dist/types/connection/command.d.ts +27 -0
- package/dist/types/connection/credential.d.ts +14 -0
- package/dist/types/connection/errors.d.ts +1 -0
- package/dist/types/connection/exchange.d.ts +9 -0
- package/dist/types/connection/failure/classify.d.ts +2 -0
- package/dist/types/connection/failure/index.d.ts +5 -0
- package/dist/types/connection/failure/model.d.ts +25 -0
- package/dist/types/connection/failure/render.d.ts +3 -0
- package/dist/types/connection/index.d.ts +8 -0
- package/dist/types/connection/reasons.d.ts +37 -0
- package/dist/types/connection/self.d.ts +37 -0
- package/dist/types/connection/session.d.ts +31 -0
- package/dist/types/connection/target.d.ts +38 -0
- package/dist/types/errors.d.ts +1 -1
- package/dist/types/lib/admin-instance.d.ts +17 -0
- package/dist/types/lib/admin-target.d.ts +11 -36
- package/dist/types/lib/config.d.ts +7 -59
- package/dist/types/lib/failure-debug.d.ts +1 -0
- package/dist/types/lib/idp.d.ts +25 -173
- package/dist/types/lib/instance-target.d.ts +1 -1
- package/dist/types/lib/instance.d.ts +30 -88
- package/dist/types/lib/log.d.ts +5 -3
- package/dist/types/lib/output.d.ts +1 -1
- package/dist/types/lib/proc.d.ts +43 -0
- package/dist/types/lib/self.d.ts +9 -0
- package/dist/types/lib/update.d.ts +109 -0
- package/dist/types/lib/validation.d.ts +4 -1
- package/package.json +15 -20
- package/src/__tests__/fixtures/publication.ts +20 -0
- package/src/admin/.spec/architecture.md +12 -5
- package/src/admin/__tests__/fixture.ts +25 -0
- package/src/admin/catalog/.spec/api.d.ts +1 -3
- package/src/admin/catalog/.spec/architecture.md +5 -4
- package/src/admin/catalog/__tests__/client.test.ts +143 -69
- package/src/admin/catalog/client.ts +40 -54
- package/src/admin/catalog/model.ts +3 -4
- package/src/admin/contract.ts +46 -0
- package/src/admin/graph/.spec/api.d.ts +4 -10
- package/src/admin/graph/nodes.ts +1 -1
- package/src/admin/instance/.spec/api.d.ts +5 -9
- package/src/admin/instance/.spec/architecture.md +7 -7
- package/src/admin/instance/__tests__/client.test.ts +157 -175
- package/src/admin/instance/client.ts +39 -128
- package/src/admin/instance/index.ts +1 -2
- package/src/admin/instance/model.ts +12 -18
- package/src/commands/__tests__/admin-instance.test.ts +11 -8
- package/src/commands/__tests__/call-describe.test.ts +46 -33
- package/src/commands/__tests__/call.test.ts +34 -0
- package/src/commands/__tests__/domain-install-operation.test.ts +7 -22
- package/src/commands/__tests__/domain-install-owned.test.ts +1 -1
- package/src/commands/__tests__/domain-list.test.ts +5 -25
- package/src/commands/__tests__/install-direct.test.ts +115 -8
- package/src/commands/__tests__/install-identity-override.test.ts +3 -14
- package/src/commands/__tests__/instance-list-rows.test.ts +1 -1
- package/src/commands/__tests__/instance-status.test.ts +139 -0
- package/src/commands/__tests__/introspect-parse.test.ts +80 -1
- package/src/commands/__tests__/logs.test.ts +250 -2
- package/src/commands/__tests__/read-commands.test.ts +62 -10
- package/src/commands/__tests__/token-ttl.test.ts +49 -4
- package/src/commands/__tests__/update.test.ts +25 -3
- package/src/commands/__tests__/view.test.ts +49 -0
- package/src/commands/call-describe.ts +60 -68
- package/src/commands/call.ts +71 -59
- package/src/commands/domain/install.ts +54 -35
- package/src/commands/domain/list.ts +1 -1
- package/src/commands/domain/publish.ts +14 -11
- package/src/commands/domain/uninstall.ts +2 -2
- package/src/commands/get.ts +14 -15
- package/src/commands/identity/__tests__/register.test.ts +3 -3
- package/src/commands/identity/register.ts +5 -4
- package/src/commands/instance/create.ts +8 -16
- package/src/commands/instance/delete.ts +12 -9
- package/src/commands/instance/list.ts +2 -2
- package/src/commands/instance/status.ts +66 -20
- package/src/commands/instance/use.ts +16 -12
- package/src/commands/introspect.ts +48 -35
- package/src/commands/logs.ts +123 -33
- package/src/commands/mutate.ts +6 -5
- package/src/commands/query.ts +35 -32
- package/src/commands/token.ts +42 -27
- package/src/commands/ui/__tests__/commands.test.ts +152 -0
- package/src/commands/ui/add.ts +51 -0
- package/src/commands/ui/doctor.ts +13 -0
- package/src/commands/ui/init.ts +38 -0
- package/src/commands/ui/list.ts +26 -0
- package/src/commands/ui/preset-apply.ts +19 -0
- package/src/commands/ui/preset-list.ts +12 -0
- package/src/commands/ui/shared.ts +25 -0
- package/src/commands/update.ts +31 -11
- package/src/commands/view.ts +82 -37
- package/src/connection/.spec/api.d.ts +7 -8
- package/src/connection/.spec/architecture.md +5 -4
- package/src/connection/.spec/flows/session.ts +2 -2
- package/src/connection/.spec/laws/connection.ts +6 -2
- package/src/connection/.spec/layout.ts +1 -0
- package/src/connection/__tests__/credential.test.ts +20 -1
- package/src/connection/__tests__/errors.test.ts +27 -20
- package/src/connection/__tests__/exchange.test.ts +30 -1
- package/src/connection/__tests__/failure-fixtures.ts +39 -0
- package/src/connection/__tests__/failure-safety.test.ts +75 -0
- package/src/connection/__tests__/self.test.ts +31 -11
- package/src/connection/__tests__/session.test.ts +3 -3
- package/src/connection/__tests__/target.test.ts +1 -0
- package/src/connection/auth.ts +13 -0
- package/src/connection/call.ts +1 -1
- package/src/connection/credential.ts +2 -2
- package/src/connection/errors.ts +1 -355
- package/src/connection/exchange.ts +47 -23
- package/src/connection/failure/classify.ts +110 -0
- package/src/connection/failure/index.ts +18 -0
- package/src/connection/failure/model.ts +28 -0
- package/src/connection/failure/render.ts +116 -0
- package/src/connection/reasons.ts +16 -0
- package/src/connection/self.ts +39 -21
- package/src/connection/session.ts +19 -8
- package/src/errors.ts +3 -2
- package/src/graph/.spec/api.d.ts +12 -5
- package/src/graph/.spec/layout.ts +9 -1
- package/src/graph/__tests__/mutation.test.ts +2 -2
- package/src/graph/__tests__/query.test.ts +25 -6
- package/src/graph/class.ts +30 -0
- package/src/graph/index.ts +1 -0
- package/src/graph/query.ts +21 -32
- package/src/identity/.spec/api.d.ts +2 -2
- package/src/identity/__tests__/registration.test.ts +1 -1
- package/src/identity/registration.ts +3 -3
- package/src/keys/__tests__/keys.test.ts +4 -0
- package/src/keys/credential.ts +1 -0
- package/src/lib/__tests__/admin-target.test.ts +12 -0
- package/src/lib/__tests__/binary.test.ts +16 -1
- package/src/lib/__tests__/command-dx.test.ts +1 -1
- package/src/lib/__tests__/domain-publication.test.ts +10 -16
- package/src/lib/__tests__/idp.test.ts +34 -4
- package/src/lib/__tests__/instance-candidates.test.ts +1 -3
- package/src/lib/__tests__/instance-target.test.ts +7 -39
- package/src/lib/__tests__/log-errors.test.ts +64 -0
- package/src/lib/__tests__/studio-server-deps.test.ts +1 -1
- package/src/lib/__tests__/update.test.ts +264 -11
- package/src/lib/__tests__/view-assets.test.ts +14 -0
- package/src/lib/__tests__/view-external-open-intent.test.ts +150 -0
- package/src/lib/__tests__/view-external-open-origins.test.ts +27 -0
- package/src/lib/__tests__/view-open-intent.test.ts +0 -2
- package/src/lib/__tests__/view-server.test.ts +26 -3
- package/src/lib/__tests__/view-session.test.ts +2 -1
- package/src/lib/admin-instance.ts +14 -8
- package/src/lib/admin-target.ts +21 -2
- package/src/lib/binary.ts +22 -5
- package/src/lib/command-dx.ts +9 -9
- package/src/lib/domain-publication.ts +9 -7
- package/src/lib/failure-debug.ts +26 -0
- package/src/lib/idp.ts +34 -3
- package/src/lib/instance-candidates.ts +2 -2
- package/src/lib/instance-target.ts +3 -14
- package/src/lib/instance.ts +16 -11
- package/src/lib/log.ts +16 -9
- package/src/lib/output.ts +1 -1
- package/src/lib/proc.ts +7 -2
- package/src/lib/provision-instance.ts +34 -57
- package/src/lib/update.ts +298 -45
- package/src/lib/validation.ts +2 -2
- package/src/lib/view/assets.ts +11 -2
- package/src/lib/view/external-open-intent.ts +41 -0
- package/src/lib/view/external-open-origins.ts +37 -0
- package/src/lib/view/host-capabilities.ts +17 -0
- package/src/lib/view/resolve.ts +1 -1
- package/src/lib/view/server.ts +18 -3
- package/src/lib/view/session.ts +2 -0
- package/src/program/.spec/api.d.ts +1 -0
- package/src/program/__tests__/program.test.ts +163 -6
- package/src/program/argv.ts +14 -0
- package/src/program/build.ts +26 -4
- package/src/program/command.ts +1 -0
- package/src/program/index.ts +1 -0
- package/src/program/registry.ts +2 -1
- package/src/setup/__tests__/instance-step.test.ts +27 -10
- package/src/setup/steps/instance.ts +22 -15
- package/src/telemetry/__tests__/recorder.test.ts +1 -0
- package/src/telemetry/recorder.ts +9 -1
- package/src/ui/.spec/api.d.ts +14 -0
- package/src/ui/.spec/architecture.md +10 -0
- package/src/ui/.spec/laws.ts +36 -0
- package/src/ui/.spec/layout.ts +16 -0
- package/src/ui/__tests__/ui.test.ts +981 -0
- package/src/ui/index.ts +13 -0
- package/src/ui/lock.ts +87 -0
- package/src/ui/model.ts +83 -0
- package/src/ui/operations.ts +710 -0
- package/src/ui/project.ts +219 -0
- package/src/ui/release.ts +321 -0
- package/src/ui/runner.ts +18 -0
- package/studio/client/dist/assets/elk-api-DjmKsHXc.js +1 -0
- package/studio/client/dist/assets/index-7YHdeCxp.js +81 -0
- package/studio/client/dist/assets/index-C0FAnwNw.css +2 -0
- package/studio/client/dist/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
- package/studio/client/dist/assets/schema-studio-BilUNb6Z.css +1 -0
- package/studio/client/dist/assets/schema-studio-CABpTfvH.js +8 -0
- package/studio/client/dist/index.html +3 -2
- package/studio/package.json +7 -13
- package/studio/server/agent/ask.test.ts +1 -1
- package/studio/server/agent/bridge/grant.test.ts +1 -1
- package/studio/server/agent/bridge/routes.test.ts +1 -1
- package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
- package/studio/server/agent/harness/gateway/token.test.ts +1 -1
- package/studio/server/agent/harness/gateway/token.ts +3 -3
- package/studio/server/agent/prompts/anchors.test.ts +32 -36
- package/studio/server/agent/prompts/anchors.ts +20 -49
- package/studio/server/agent/prompts/system.test.ts +3 -2
- package/studio/server/agent/prompts/system.ts +3 -3
- package/studio/server/agent/routes.test.ts +1 -1
- package/studio/server/agent/run/coordinator.test.ts +1 -1
- package/studio/server/api/canvas.ts +0 -1
- package/studio/server/api-agent-loadout.test.ts +1 -1
- package/studio/server/api.test.ts +1 -1
- package/studio/server/cache.test.ts +23 -12
- package/studio/server/cache.ts +12 -15
- package/studio/server/domain.test.ts +50 -68
- package/studio/server/domain.ts +82 -63
- package/studio/server/handoff/copy.ts +1 -1
- package/studio/server/index.ts +1 -1
- package/studio/server/instances/probe.test.ts +1 -1
- package/studio/server/introspect/anatomy/views/routes.ts +46 -37
- package/studio/server/introspect/anatomy/views.ts +9 -14
- package/studio/server/introspect/anatomy-extras.test.ts +26 -42
- package/studio/server/introspect/anatomy.test.ts +22 -14
- package/studio/server/introspect/anatomy.ts +6 -9
- package/studio/server/introspect/bundle.ts +0 -3
- package/studio/server/introspect/canonical-schema.test.ts +94 -389
- package/studio/server/introspect/canonical-schema.ts +227 -457
- package/studio/server/introspect/core-extractor.ts +18 -127
- package/studio/server/introspect/core.ts +1 -2
- package/studio/server/introspect/diff.test.ts +11 -7
- package/studio/server/introspect/diff.ts +18 -55
- package/studio/server/introspect/extractor.ts +22 -117
- package/studio/server/introspect/overlay-tsmorph.test.ts +119 -240
- package/studio/server/introspect/overlay.test.ts +26 -54
- package/studio/server/introspect/overlay.ts +10 -37
- package/studio/server/introspect/revision.test.ts +1 -1
- package/studio/server/introspect/revision.ts +1 -1
- package/studio/server/introspect/runtime.test.ts +65 -183
- package/studio/server/introspect/runtime.ts +8 -25
- package/studio/server/introspect/schema-ir-json.test.ts +70 -0
- package/studio/server/introspect/schema-ir-json.ts +38 -68
- package/studio/server/introspect/schema-refs.test.ts +43 -88
- package/studio/server/introspect/schema-refs.ts +14 -49
- package/studio/server/introspect/source-overlay/handlers.ts +116 -636
- package/studio/server/introspect/source-overlay/kernel-calls.ts +0 -3
- package/studio/server/introspect/source-overlay/spans.ts +15 -45
- package/studio/server/state/baseline.test.ts +14 -9
- package/studio/server/state/baseline.ts +3 -5
- package/studio/server/state/visibility.ts +1 -5
- package/studio/server/views/model.ts +3 -3
- package/studio/server/views/target.test.ts +17 -74
- package/studio/server/views/target.ts +24 -50
- package/studio/server/watch.test.ts +11 -11
- package/studio/server/watch.ts +8 -4
- package/studio/server/workspace-watch.ts +2 -2
- package/studio/shared/contracts/schema.ts +38 -107
- package/studio/shared/contracts/surface.test.ts +13 -15
- package/studio/shared/contracts/workspace.ts +2 -11
- package/studio/shared/schema/identity.test.ts +20 -44
- package/studio/shared/schema/identity.ts +9 -23
- package/viewer/dist/main.js +30 -51
- package/.check-workspace.cjs +0 -40
- package/src/admin/binding.ts +0 -168
- package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
- package/studio/client/dist/assets/elk-api-lwhFo7vB.js +0 -1
- package/studio/client/dist/assets/index-B-c-smo9.js +0 -8
- package/studio/client/dist/assets/index-BckHuAWk.js +0 -81
- package/studio/client/dist/assets/index-C4aNQ6dz.css +0 -1
- package/studio/client/dist/assets/index-ChOr3yP0.css +0 -1
- package/studio/server/introspect/anatomy/views/legacy.ts +0 -232
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { HostCapabilities } from '@astrale-os/shell'
|
|
2
|
+
|
|
3
|
+
import { admitHostCapabilities } from '@astrale-os/shell'
|
|
4
|
+
|
|
5
|
+
/** Build the exact host grant handed to a CLI-hosted View session. */
|
|
6
|
+
export function viewHostCapabilities(externalOrigins: readonly string[]): HostCapabilities {
|
|
7
|
+
return admitHostCapabilities({
|
|
8
|
+
version: 1,
|
|
9
|
+
navigation: {
|
|
10
|
+
openView: {},
|
|
11
|
+
...(externalOrigins.length === 0 ? {} : { external: { origins: externalOrigins } }),
|
|
12
|
+
},
|
|
13
|
+
actions: {},
|
|
14
|
+
browser: {},
|
|
15
|
+
access: {},
|
|
16
|
+
})
|
|
17
|
+
}
|
package/src/lib/view/resolve.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ResolvedView as SessionResolvedView } from '@astrale-os/
|
|
1
|
+
import type { ResolvedView as SessionResolvedView } from '@astrale-os/sdk/client/session'
|
|
2
2
|
import type { ResolvedView } from '@astrale-os/shell'
|
|
3
3
|
|
|
4
4
|
import { Path } from '@astrale-os/sdk/graph/path'
|
package/src/lib/view/server.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { AuthApi } from '@astrale-os/sdk/auth'
|
|
2
|
+
import type { IssuerId } from '@astrale-os/sdk/auth'
|
|
1
3
|
import type { ReadableStream as WebReadableStream } from 'node:stream/web'
|
|
2
4
|
|
|
3
5
|
import { readFile } from 'node:fs/promises'
|
|
@@ -23,8 +25,9 @@ export { ensureViewerAssets, viewerDistDir } from './assets'
|
|
|
23
25
|
* heartbeat keeps a live session alive.
|
|
24
26
|
*/
|
|
25
27
|
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
+
const VIEW_TOKEN_TTL_SECONDS = 4 * 60
|
|
29
|
+
const TOKEN_REFRESH_MARGIN_MS = 60_000
|
|
30
|
+
const FALLBACK_TOKEN_TTL_MS = VIEW_TOKEN_TTL_SECONDS * 1_000
|
|
28
31
|
const IDLE_SWEEP_MS = 60_000
|
|
29
32
|
|
|
30
33
|
export type PageStatus = { state: string; error?: string; at: string }
|
|
@@ -44,7 +47,7 @@ export function startViewServer(config: ViewServeConfig): Server {
|
|
|
44
47
|
async function freshGrant(): Promise<TokenGrant> {
|
|
45
48
|
if (grant && grant.expiresAt - Date.now() > TOKEN_REFRESH_MARGIN_MS) return grant
|
|
46
49
|
grant = await withClientSession(config.kernel, async ({ auth, target }) => {
|
|
47
|
-
const token = await auth
|
|
50
|
+
const token = await mintViewCredential(auth, target.kernelIssuer)
|
|
48
51
|
return {
|
|
49
52
|
token,
|
|
50
53
|
expiresAt: jwtExpiry(token) ?? Date.now() + FALLBACK_TOKEN_TTL_MS,
|
|
@@ -98,6 +101,7 @@ export function startViewServer(config: ViewServeConfig): Server {
|
|
|
98
101
|
identity: session.identity ?? null,
|
|
99
102
|
instance: session.instance ?? null,
|
|
100
103
|
sessionId: session.id,
|
|
104
|
+
externalOrigins: config.externalOrigins,
|
|
101
105
|
})
|
|
102
106
|
return
|
|
103
107
|
}
|
|
@@ -182,6 +186,17 @@ export function startViewServer(config: ViewServeConfig): Server {
|
|
|
182
186
|
return server
|
|
183
187
|
}
|
|
184
188
|
|
|
189
|
+
/** Mint a short-lived credential from the selected CLI identity proof. */
|
|
190
|
+
export async function mintViewCredential(
|
|
191
|
+
auth: Pick<AuthApi, 'mint'>,
|
|
192
|
+
audience: IssuerId,
|
|
193
|
+
): Promise<string> {
|
|
194
|
+
return auth.mint({
|
|
195
|
+
audience,
|
|
196
|
+
ttlSeconds: VIEW_TOKEN_TTL_SECONDS,
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
|
|
185
200
|
function jwtExpiry(token: string): number | null {
|
|
186
201
|
try {
|
|
187
202
|
const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64url').toString('utf8')) as {
|
package/src/lib/view/session.ts
CHANGED
|
@@ -41,6 +41,8 @@ export type ViewServeConfig = {
|
|
|
41
41
|
* handles CORS and self-signed local CAs.
|
|
42
42
|
*/
|
|
43
43
|
proxy: { kernelUrl: string; issuer: string; caFile?: string; direct: boolean }
|
|
44
|
+
/** Exact HTTPS origins the operator consented to open from this View session. */
|
|
45
|
+
externalOrigins: readonly string[]
|
|
44
46
|
idleMs: number
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -5,7 +5,7 @@ import { createHash } from 'node:crypto'
|
|
|
5
5
|
import { existsSync, readFileSync } from 'node:fs'
|
|
6
6
|
import { join } from 'node:path'
|
|
7
7
|
|
|
8
|
-
import { buildProgram } from '../index'
|
|
8
|
+
import { buildProgram, normalizeRootVersionArgv } from '../index'
|
|
9
9
|
|
|
10
10
|
// Help output is the public CLI contract: version, spec anchors, and skill mirror stay in sync.
|
|
11
11
|
|
|
@@ -181,13 +181,21 @@ describe('program composition', () => {
|
|
|
181
181
|
'status',
|
|
182
182
|
'studio',
|
|
183
183
|
'token',
|
|
184
|
+
'ui',
|
|
185
|
+
'ui add',
|
|
186
|
+
'ui doctor',
|
|
187
|
+
'ui init',
|
|
188
|
+
'ui list',
|
|
189
|
+
'ui preset',
|
|
190
|
+
'ui preset apply',
|
|
191
|
+
'ui preset list',
|
|
184
192
|
'update',
|
|
185
193
|
'use',
|
|
186
194
|
'view',
|
|
187
195
|
'whoami',
|
|
188
196
|
])
|
|
189
197
|
expect(createHash('sha256').update(JSON.stringify(surface)).digest('hex')).toBe(
|
|
190
|
-
'
|
|
198
|
+
'51546dd7ea5cbf25efbd41f0cd09ceb480abad5be489fc16743500a18e3a02c6',
|
|
191
199
|
)
|
|
192
200
|
})
|
|
193
201
|
|
|
@@ -213,6 +221,19 @@ describe('help contract — no internal SPEC anchors leak to users', () => {
|
|
|
213
221
|
|
|
214
222
|
expect(offenders).toEqual([])
|
|
215
223
|
})
|
|
224
|
+
|
|
225
|
+
test('Kernel and Instance vocabulary does not expose retired Host or Interface concepts', async () => {
|
|
226
|
+
const program = await buildProgram()
|
|
227
|
+
const help = allCommands(program)
|
|
228
|
+
.map((command) => command.helpInformation())
|
|
229
|
+
.join('\n')
|
|
230
|
+
|
|
231
|
+
expect(help).not.toContain('host.astrale.ai')
|
|
232
|
+
expect(help).not.toContain(':interface.')
|
|
233
|
+
expect(help).not.toContain('Class or Interface')
|
|
234
|
+
expect(help).not.toContain('emulated host')
|
|
235
|
+
expect(help).not.toContain('Host shell')
|
|
236
|
+
})
|
|
216
237
|
})
|
|
217
238
|
|
|
218
239
|
describe('help contract — IdP/auth surface is registered', () => {
|
|
@@ -251,21 +272,24 @@ describe('help contract — admin target surface is registered', () => {
|
|
|
251
272
|
expect(adminUse?.helpInformation()).not.toContain('--issuer <url>')
|
|
252
273
|
})
|
|
253
274
|
|
|
254
|
-
test('instance create
|
|
275
|
+
test('instance create delegates infrastructure placement to Admin', async () => {
|
|
255
276
|
const program = await buildProgram()
|
|
256
277
|
const instanceCreate = allCommands(program).find((command) => command.name() === 'create')
|
|
257
278
|
const help = instanceCreate?.helpInformation() ?? ''
|
|
258
279
|
|
|
259
|
-
expect(help).toContain('
|
|
280
|
+
expect(help).toContain('Provision an instance through the Admin control plane')
|
|
281
|
+
expect(help).not.toContain('Host')
|
|
282
|
+
expect(help).not.toContain('Fleet')
|
|
283
|
+
expect(help).not.toContain('--host-id')
|
|
260
284
|
expect(help).not.toContain('--no-use')
|
|
261
|
-
expect(help).not.toContain('Instance.init
|
|
285
|
+
expect(help).not.toContain('Instance.init')
|
|
262
286
|
})
|
|
263
287
|
})
|
|
264
288
|
|
|
265
289
|
describe('help contract — connect-only command surface', () => {
|
|
266
290
|
test('runtime management commands are not registered', async () => {
|
|
267
291
|
const program = await buildProgram()
|
|
268
|
-
const names =
|
|
292
|
+
const names = program.commands.map((command) => command.name())
|
|
269
293
|
|
|
270
294
|
// `logs` and `domain` have managed/admin meanings, so only retired verbs are absent.
|
|
271
295
|
for (const removed of [
|
|
@@ -287,6 +311,139 @@ describe('help contract — connect-only command surface', () => {
|
|
|
287
311
|
})
|
|
288
312
|
})
|
|
289
313
|
|
|
314
|
+
describe('help contract — UI is project tooling', () => {
|
|
315
|
+
test('add recovery guidance uses only registered UI commands', async () => {
|
|
316
|
+
const program = await buildProgram()
|
|
317
|
+
const uiAdd = program.commands
|
|
318
|
+
.find((command) => command.name() === 'ui')
|
|
319
|
+
?.commands.find((command) => command.name() === 'add')
|
|
320
|
+
let help = ''
|
|
321
|
+
uiAdd?.configureOutput({
|
|
322
|
+
writeOut: (chunk) => {
|
|
323
|
+
help += chunk ?? ''
|
|
324
|
+
},
|
|
325
|
+
})
|
|
326
|
+
uiAdd?.outputHelp()
|
|
327
|
+
|
|
328
|
+
expect(help).toContain('astrale ui doctor')
|
|
329
|
+
expect(help).toContain('--overwrite --yes')
|
|
330
|
+
expect(help).not.toMatch(/\b(?:astrale ui )?diff\b/u)
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
test('keeps the documented root version alias operational', async () => {
|
|
334
|
+
const program = await buildProgram()
|
|
335
|
+
let stdout = ''
|
|
336
|
+
program.exitOverride().configureOutput({
|
|
337
|
+
writeOut: (chunk) => {
|
|
338
|
+
stdout += chunk ?? ''
|
|
339
|
+
},
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
await expect(
|
|
343
|
+
program.parseAsync(normalizeRootVersionArgv(['node', 'astrale', '--version'])),
|
|
344
|
+
).rejects.toMatchObject({ code: 'commander.version' })
|
|
345
|
+
expect(stdout.trim()).toBe(program.version() ?? '')
|
|
346
|
+
expect(program.helpInformation()).toMatch(/root alias:\s+--version/u)
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
test('routes root and subcommand version flags to their exact owners', async () => {
|
|
350
|
+
const program = await buildProgram()
|
|
351
|
+
const uiList = program.commands
|
|
352
|
+
.find((command) => command.name() === 'ui')
|
|
353
|
+
?.commands.find((command) => command.name() === 'list')
|
|
354
|
+
let observedVersion: unknown
|
|
355
|
+
uiList?.action((_query, options) => {
|
|
356
|
+
observedVersion = options.version
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
const uiArgv = ['node', 'astrale', 'ui', 'list', 'chart', '--version', '0.3.0-beta.1']
|
|
360
|
+
expect(normalizeRootVersionArgv(uiArgv)).toEqual(uiArgv)
|
|
361
|
+
expect(normalizeRootVersionArgv(['node', 'astrale', '--version'])).toEqual([
|
|
362
|
+
'node',
|
|
363
|
+
'astrale',
|
|
364
|
+
'--cli-version',
|
|
365
|
+
])
|
|
366
|
+
expect(normalizeRootVersionArgv(['node', 'astrale', '--ci', '--version'])).toEqual([
|
|
367
|
+
'node',
|
|
368
|
+
'astrale',
|
|
369
|
+
'--ci',
|
|
370
|
+
'--cli-version',
|
|
371
|
+
])
|
|
372
|
+
|
|
373
|
+
await program.parseAsync(uiArgv)
|
|
374
|
+
|
|
375
|
+
expect(observedVersion).toBe('0.3.0-beta.1')
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
test('routes explicit versions with positional inputs for UI init and update', async () => {
|
|
379
|
+
const program = await buildProgram()
|
|
380
|
+
const uiInit = program.commands
|
|
381
|
+
.find((command) => command.name() === 'ui')
|
|
382
|
+
?.commands.find((command) => command.name() === 'init')
|
|
383
|
+
const update = program.commands.find((command) => command.name() === 'update')
|
|
384
|
+
let initializedPath: unknown
|
|
385
|
+
let initializedVersion: unknown
|
|
386
|
+
let updateVersion: unknown
|
|
387
|
+
uiInit?.action((projectPath, options) => {
|
|
388
|
+
initializedPath = projectPath
|
|
389
|
+
initializedVersion = options.version
|
|
390
|
+
})
|
|
391
|
+
update?.action((options) => {
|
|
392
|
+
updateVersion = options.version
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
await program.parseAsync([
|
|
396
|
+
'node',
|
|
397
|
+
'astrale',
|
|
398
|
+
'ui',
|
|
399
|
+
'init',
|
|
400
|
+
'./app',
|
|
401
|
+
'--version',
|
|
402
|
+
'0.3.0-beta.1',
|
|
403
|
+
])
|
|
404
|
+
expect(initializedPath).toBe('./app')
|
|
405
|
+
expect(initializedVersion).toBe('0.3.0-beta.1')
|
|
406
|
+
|
|
407
|
+
await program.parseAsync(['node', 'astrale', 'update', '--version', '1.0.0-beta.13'])
|
|
408
|
+
expect(updateVersion).toBe('1.0.0-beta.13')
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
test('continues to admit global machine flags after a subcommand', async () => {
|
|
412
|
+
const program = await buildProgram()
|
|
413
|
+
const uiList = program.commands
|
|
414
|
+
.find((command) => command.name() === 'ui')
|
|
415
|
+
?.commands.find((command) => command.name() === 'list')
|
|
416
|
+
let invoked = false
|
|
417
|
+
uiList?.action(() => {
|
|
418
|
+
invoked = true
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
await program.parseAsync(['node', 'astrale', 'ui', 'list', '--ci', '--no-prompt'])
|
|
422
|
+
|
|
423
|
+
expect(invoked).toBe(true)
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
test('UI commands are local-only and add accepts zero or more canonical addresses', async () => {
|
|
427
|
+
const program = await buildProgram()
|
|
428
|
+
const ui = program.commands.find((command) => command.name() === 'ui')
|
|
429
|
+
const add = ui?.commands.find((command) => command.name() === 'add')
|
|
430
|
+
|
|
431
|
+
expect(ui?.commands.map((command) => command.name())).toEqual([
|
|
432
|
+
'init',
|
|
433
|
+
'list',
|
|
434
|
+
'add',
|
|
435
|
+
'doctor',
|
|
436
|
+
'preset',
|
|
437
|
+
])
|
|
438
|
+
expect(add?.helpInformation()).toContain('[items...]')
|
|
439
|
+
for (const command of ui?.commands ?? []) {
|
|
440
|
+
const help = command.helpInformation()
|
|
441
|
+
expect(help).not.toContain('--url <url>')
|
|
442
|
+
expect(help).not.toContain('--anonymous')
|
|
443
|
+
}
|
|
444
|
+
})
|
|
445
|
+
})
|
|
446
|
+
|
|
290
447
|
describe('help contract — read command split', () => {
|
|
291
448
|
test('get is point-read only and query owns structured read flags', async () => {
|
|
292
449
|
const program = await buildProgram()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keep `astrale --version` as the familiar root spelling without claiming the
|
|
3
|
+
* same flag after a subcommand. Commands such as `ui list` and `update` own
|
|
4
|
+
* their release `--version` value.
|
|
5
|
+
*/
|
|
6
|
+
export function normalizeRootVersionArgv(argv: readonly string[]): string[] {
|
|
7
|
+
const normalized = [...argv]
|
|
8
|
+
const commandIndex = normalized.findIndex((token, index) => index >= 2 && !token.startsWith('-'))
|
|
9
|
+
const versionIndex = normalized.indexOf('--version', 2)
|
|
10
|
+
if (versionIndex !== -1 && (commandIndex === -1 || versionIndex < commandIndex)) {
|
|
11
|
+
normalized[versionIndex] = '--cli-version'
|
|
12
|
+
}
|
|
13
|
+
return normalized
|
|
14
|
+
}
|
package/src/program/build.ts
CHANGED
|
@@ -19,7 +19,7 @@ export async function buildProgram(): Promise<Command> {
|
|
|
19
19
|
.description('Astrale CLI — connect to existing Astrale kernels')
|
|
20
20
|
// Single source of truth = package.json (bumped by release-please together
|
|
21
21
|
// with .release-please-manifest.json). Never hand-write a version literal.
|
|
22
|
-
.version(pkg.version)
|
|
22
|
+
.version(pkg.version, '-V, --cli-version', 'output the CLI version (root alias: --version)')
|
|
23
23
|
.showSuggestionAfterError(true)
|
|
24
24
|
.addOption(new Option('--ci', 'Machine mode: no prompts, structured errors on stderr'))
|
|
25
25
|
.addOption(new Option('--no-prompt', 'Disable interactive prompts'))
|
|
@@ -63,6 +63,27 @@ export async function buildProgram(): Promise<Command> {
|
|
|
63
63
|
registerCommand(program, (await import('../commands/view-serve')).default)
|
|
64
64
|
registerCommand(program, (await import('../commands/studio')).default)
|
|
65
65
|
|
|
66
|
+
registerGroup(program, {
|
|
67
|
+
name: 'ui',
|
|
68
|
+
description: 'Initialize and install Astrale UI in local applications',
|
|
69
|
+
commands: [
|
|
70
|
+
(await import('../commands/ui/init')).default,
|
|
71
|
+
(await import('../commands/ui/list')).default,
|
|
72
|
+
(await import('../commands/ui/add')).default,
|
|
73
|
+
(await import('../commands/ui/doctor')).default,
|
|
74
|
+
],
|
|
75
|
+
subgroups: [
|
|
76
|
+
{
|
|
77
|
+
name: 'preset',
|
|
78
|
+
description: 'List and apply qualified Astrale UI presets',
|
|
79
|
+
commands: [
|
|
80
|
+
(await import('../commands/ui/preset-list')).default,
|
|
81
|
+
(await import('../commands/ui/preset-apply')).default,
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
})
|
|
86
|
+
|
|
66
87
|
registerGroup(program, {
|
|
67
88
|
name: 'instance',
|
|
68
89
|
description: 'Manage admin-provisioned instances and local bookmarks',
|
|
@@ -154,12 +175,13 @@ Command groups:
|
|
|
154
175
|
Getting started setup (sign in, pick an instance, equip your workspace)
|
|
155
176
|
Kernel get, mutate, call, query, introspect, logs, view, token
|
|
156
177
|
Management admin, instance, domain, identity, auth, idp, update
|
|
178
|
+
Application ui (initialize, inspect, and install Astrale UI source)
|
|
157
179
|
Agent browser (drive the GUI via agent-browser)
|
|
158
180
|
Studio studio (launch the local Domain Studio GUI for a workspace)
|
|
159
181
|
|
|
160
182
|
Path syntax:
|
|
161
183
|
/:origin Domain root
|
|
162
|
-
/:origin:class.Name Class node
|
|
184
|
+
/:origin:class.Name Class node
|
|
163
185
|
/:origin:class.Name:method Static callable
|
|
164
186
|
<nodePath>::method Instance method dispatch — double colon ::
|
|
165
187
|
@nodeId Reference a node by its UID
|
|
@@ -172,9 +194,9 @@ Examples:
|
|
|
172
194
|
$ astrale instance bookmark staging --url https://kernel.example.com
|
|
173
195
|
$ astrale instance create my-app
|
|
174
196
|
$ astrale instance status staging
|
|
175
|
-
$ astrale token --audience shell.astrale.ai
|
|
197
|
+
$ astrale token --audience shell.astrale.ai
|
|
176
198
|
$ astrale query /:notes.example.dev:class.Note --limit 50
|
|
177
|
-
$ astrale introspect /:
|
|
199
|
+
$ astrale introspect /:kernel.astrale.ai:class.Identity:whois
|
|
178
200
|
$ astrale query --file query.v6.json --cursor "$CURSOR"
|
|
179
201
|
`,
|
|
180
202
|
)
|
package/src/program/command.ts
CHANGED
package/src/program/index.ts
CHANGED
package/src/program/registry.ts
CHANGED
|
@@ -18,7 +18,8 @@ export function registerCommand(parent: Command, def: CommandDefinition): void {
|
|
|
18
18
|
|
|
19
19
|
if (def.arguments) {
|
|
20
20
|
for (const arg of def.arguments) {
|
|
21
|
-
const
|
|
21
|
+
const name = arg.variadic ? `${arg.name}...` : arg.name
|
|
22
|
+
const bracket = arg.required !== false ? `<${name}>` : `[${name}]`
|
|
22
23
|
cmd.argument(bracket, arg.description)
|
|
23
24
|
}
|
|
24
25
|
}
|
|
@@ -27,11 +27,15 @@ function instance(slug: string, state: OwnedInstanceInfo['state'] = 'ready'): Ow
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
function inventory(instances: OwnedInstanceInfo[], identity?: string) {
|
|
31
|
+
return { instances, ...(identity ? { identity } : {}) }
|
|
32
|
+
}
|
|
33
|
+
|
|
30
34
|
function dependencies(
|
|
31
35
|
overrides: Partial<InstanceSetupDependencies> = {},
|
|
32
36
|
): InstanceSetupDependencies {
|
|
33
37
|
return {
|
|
34
|
-
fetchOwned: mock(async () => []),
|
|
38
|
+
fetchOwned: mock(async () => inventory([])),
|
|
35
39
|
adopt: mock(async () => {}),
|
|
36
40
|
selectReady: mock(async (instances) => instances[0] ?? null),
|
|
37
41
|
confirmCreate: mock(async () => true),
|
|
@@ -48,12 +52,14 @@ describe('setup owned-instance reconciliation', () => {
|
|
|
48
52
|
test('silently adopts the sole owned ready instance', async () => {
|
|
49
53
|
const ready = instance('only')
|
|
50
54
|
const failed = instance('old-attempt', 'failed')
|
|
51
|
-
const deps = dependencies({
|
|
55
|
+
const deps = dependencies({
|
|
56
|
+
fetchOwned: mock(async () => inventory([failed, ready], 'manager')),
|
|
57
|
+
})
|
|
52
58
|
|
|
53
59
|
await expect(ensureOwnedInstance(ctx, deps)).resolves.toBe('fixed')
|
|
54
60
|
|
|
55
61
|
expect(deps.adopt).toHaveBeenCalledTimes(1)
|
|
56
|
-
expect(deps.adopt).toHaveBeenCalledWith(ready)
|
|
62
|
+
expect(deps.adopt).toHaveBeenCalledWith(ready, 'manager')
|
|
57
63
|
expect(deps.selectReady).not.toHaveBeenCalled()
|
|
58
64
|
expect(deps.confirmCreate).not.toHaveBeenCalled()
|
|
59
65
|
expect(deps.provision).not.toHaveBeenCalled()
|
|
@@ -64,14 +70,14 @@ describe('setup owned-instance reconciliation', () => {
|
|
|
64
70
|
const second = instance('second')
|
|
65
71
|
const pending = instance('pending', 'provisioning')
|
|
66
72
|
const deps = dependencies({
|
|
67
|
-
fetchOwned: mock(async () => [first, pending, second]),
|
|
73
|
+
fetchOwned: mock(async () => inventory([first, pending, second], 'manager')),
|
|
68
74
|
selectReady: mock(async (instances) => instances[1] ?? null),
|
|
69
75
|
})
|
|
70
76
|
|
|
71
77
|
await expect(ensureOwnedInstance(ctx, deps)).resolves.toBe('fixed')
|
|
72
78
|
|
|
73
79
|
expect(deps.selectReady).toHaveBeenCalledWith([first, second])
|
|
74
|
-
expect(deps.adopt).toHaveBeenCalledWith(second)
|
|
80
|
+
expect(deps.adopt).toHaveBeenCalledWith(second, 'manager')
|
|
75
81
|
expect(deps.confirmCreate).not.toHaveBeenCalled()
|
|
76
82
|
expect(deps.provision).not.toHaveBeenCalled()
|
|
77
83
|
})
|
|
@@ -80,7 +86,7 @@ describe('setup owned-instance reconciliation', () => {
|
|
|
80
86
|
const first = instance('first')
|
|
81
87
|
const second = instance('second')
|
|
82
88
|
const deps = dependencies({
|
|
83
|
-
fetchOwned: mock(async () => [first, second]),
|
|
89
|
+
fetchOwned: mock(async () => inventory([first, second])),
|
|
84
90
|
selectReady: mock(async () => null),
|
|
85
91
|
})
|
|
86
92
|
|
|
@@ -165,7 +171,9 @@ describe('setup owned-instance reconciliation', () => {
|
|
|
165
171
|
phase: 'installing:default-domains',
|
|
166
172
|
}
|
|
167
173
|
const failed = { ...instance('broken', 'failed'), error: 'postInstall failed' }
|
|
168
|
-
const deps = dependencies({
|
|
174
|
+
const deps = dependencies({
|
|
175
|
+
fetchOwned: mock(async () => inventory([provisioning, failed])),
|
|
176
|
+
})
|
|
169
177
|
const logged: string[] = []
|
|
170
178
|
const original = console.log
|
|
171
179
|
console.log = mock((...args: unknown[]) => {
|
|
@@ -218,10 +226,10 @@ describe('owned-instance adoption', () => {
|
|
|
218
226
|
console.log = mock(() => {})
|
|
219
227
|
|
|
220
228
|
try {
|
|
221
|
-
await adoptOwnedInstance(owned, {
|
|
229
|
+
await adoptOwnedInstance(owned, 'manager', {
|
|
222
230
|
upsert: mock(async (...args) => {
|
|
223
231
|
calls.push(['upsert', ...args])
|
|
224
|
-
return {}
|
|
232
|
+
return { entry: {} }
|
|
225
233
|
}),
|
|
226
234
|
activate: mock(async (...args) => {
|
|
227
235
|
calls.push(['activate', ...args])
|
|
@@ -232,7 +240,16 @@ describe('owned-instance adoption', () => {
|
|
|
232
240
|
}
|
|
233
241
|
|
|
234
242
|
expect(calls).toEqual([
|
|
235
|
-
[
|
|
243
|
+
[
|
|
244
|
+
'upsert',
|
|
245
|
+
{
|
|
246
|
+
key: 'existing',
|
|
247
|
+
slug: 'existing',
|
|
248
|
+
url: 'https://existing.eu.astrale.ai',
|
|
249
|
+
organizationId: 'org_existing',
|
|
250
|
+
defaultIdentity: 'manager',
|
|
251
|
+
},
|
|
252
|
+
],
|
|
236
253
|
['activate', 'existing'],
|
|
237
254
|
])
|
|
238
255
|
})
|
|
@@ -4,7 +4,7 @@ import type { OwnedInstanceInfo } from '../../lib/admin-instance'
|
|
|
4
4
|
import type { SetupContext, SetupStep } from '../types'
|
|
5
5
|
|
|
6
6
|
import { AstraleError } from '../../errors'
|
|
7
|
-
import {
|
|
7
|
+
import { listOwnedInstancesWithIdentity } from '../../lib/admin-instance'
|
|
8
8
|
import { normalizeInstanceKernelUrl, setActive, upsertManagedBookmark } from '../../lib/instance'
|
|
9
9
|
import { readLocalStatus } from '../../lib/local-status'
|
|
10
10
|
import { log, withSpinner } from '../../lib/log'
|
|
@@ -14,8 +14,11 @@ import { provisionInstance, type ProvisionResult } from '../../lib/provision-ins
|
|
|
14
14
|
import { guiOrigin, slugError } from '../util'
|
|
15
15
|
|
|
16
16
|
export type InstanceSetupDependencies = {
|
|
17
|
-
fetchOwned: (ctx: SetupContext) => Promise<
|
|
18
|
-
|
|
17
|
+
fetchOwned: (ctx: SetupContext) => Promise<{
|
|
18
|
+
readonly instances: OwnedInstanceInfo[]
|
|
19
|
+
readonly identity?: string
|
|
20
|
+
}>
|
|
21
|
+
adopt: (info: OwnedInstanceInfo, identity?: string) => Promise<void>
|
|
19
22
|
selectReady: (instances: OwnedInstanceInfo[]) => Promise<OwnedInstanceInfo | null>
|
|
20
23
|
confirmCreate: () => Promise<boolean>
|
|
21
24
|
promptSlug: () => Promise<string | undefined>
|
|
@@ -23,12 +26,7 @@ export type InstanceSetupDependencies = {
|
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
export type OwnedInstanceAdoptionDependencies = {
|
|
26
|
-
upsert:
|
|
27
|
-
key: string,
|
|
28
|
-
slug: string,
|
|
29
|
-
url: string,
|
|
30
|
-
organizationId?: string,
|
|
31
|
-
) => Promise<{ repointedFrom?: string }>
|
|
29
|
+
upsert: typeof upsertManagedBookmark
|
|
32
30
|
activate: (slug: string) => Promise<unknown>
|
|
33
31
|
}
|
|
34
32
|
|
|
@@ -41,12 +39,19 @@ function hero(slug: string, kernelUrl: string): void {
|
|
|
41
39
|
|
|
42
40
|
export async function adoptOwnedInstance(
|
|
43
41
|
info: OwnedInstanceInfo,
|
|
42
|
+
defaultIdentity?: string,
|
|
44
43
|
deps: OwnedInstanceAdoptionDependencies = {
|
|
45
44
|
upsert: upsertManagedBookmark,
|
|
46
45
|
activate: setActive,
|
|
47
46
|
},
|
|
48
47
|
): Promise<void> {
|
|
49
|
-
const { repointedFrom } = await deps.upsert(
|
|
48
|
+
const { repointedFrom } = await deps.upsert({
|
|
49
|
+
key: info.slug,
|
|
50
|
+
slug: info.slug,
|
|
51
|
+
url: info.url,
|
|
52
|
+
...(info.organizationId ? { organizationId: info.organizationId } : {}),
|
|
53
|
+
...(defaultIdentity ? { defaultIdentity } : {}),
|
|
54
|
+
})
|
|
50
55
|
await deps.activate(info.slug)
|
|
51
56
|
if (repointedFrom) {
|
|
52
57
|
log.warn(
|
|
@@ -61,7 +66,7 @@ function defaultDependencies(ctx: SetupContext): InstanceSetupDependencies {
|
|
|
61
66
|
return {
|
|
62
67
|
fetchOwned: (setupCtx) =>
|
|
63
68
|
withSpinner('Checking for existing instances', !setupCtx.machine, () =>
|
|
64
|
-
|
|
69
|
+
listOwnedInstancesWithIdentity(setupCtx.opts),
|
|
65
70
|
),
|
|
66
71
|
adopt: adoptOwnedInstance,
|
|
67
72
|
selectReady: (instances) =>
|
|
@@ -90,9 +95,9 @@ export async function ensureOwnedInstance(
|
|
|
90
95
|
ctx: SetupContext,
|
|
91
96
|
deps: InstanceSetupDependencies = defaultDependencies(ctx),
|
|
92
97
|
): Promise<'fixed' | 'skipped'> {
|
|
93
|
-
let
|
|
98
|
+
let inventory: Awaited<ReturnType<InstanceSetupDependencies['fetchOwned']>>
|
|
94
99
|
try {
|
|
95
|
-
|
|
100
|
+
inventory = await deps.fetchOwned(ctx)
|
|
96
101
|
} catch (cause) {
|
|
97
102
|
const detail = cause instanceof Error ? cause.message : String(cause)
|
|
98
103
|
throw new AstraleError(
|
|
@@ -102,9 +107,11 @@ export async function ensureOwnedInstance(
|
|
|
102
107
|
)
|
|
103
108
|
}
|
|
104
109
|
|
|
110
|
+
const owned = inventory.instances
|
|
111
|
+
|
|
105
112
|
const ready = owned.filter((info) => info.state === 'ready')
|
|
106
113
|
if (ready.length === 1) {
|
|
107
|
-
await deps.adopt(ready[0]
|
|
114
|
+
await deps.adopt(ready[0]!, inventory.identity)
|
|
108
115
|
return 'fixed'
|
|
109
116
|
}
|
|
110
117
|
|
|
@@ -114,7 +121,7 @@ export async function ensureOwnedInstance(
|
|
|
114
121
|
log.dim(' Skipped — no active instance set.')
|
|
115
122
|
return 'skipped'
|
|
116
123
|
}
|
|
117
|
-
await deps.adopt(choice)
|
|
124
|
+
await deps.adopt(choice, inventory.identity)
|
|
118
125
|
return 'fixed'
|
|
119
126
|
}
|
|
120
127
|
|
|
@@ -91,6 +91,7 @@ describe('beginInvocation', () => {
|
|
|
91
91
|
test('writes nothing for help/version-only invocations', () => {
|
|
92
92
|
beginInvocation(['node', 'astrale', '--help'])(0)
|
|
93
93
|
beginInvocation(['node', 'astrale', '--version'])(0)
|
|
94
|
+
beginInvocation(['node', 'astrale', '--cli-version'])(0)
|
|
94
95
|
beginInvocation(['node', 'astrale'])(0)
|
|
95
96
|
expect(listSessions()).toHaveLength(0)
|
|
96
97
|
})
|
|
@@ -17,7 +17,15 @@ import { eventsPath } from './store'
|
|
|
17
17
|
export type Finalizer = (exitCode: number, errorName?: string) => void
|
|
18
18
|
|
|
19
19
|
const NOOP: Finalizer = () => {}
|
|
20
|
-
const HELP_VERSION = new Set([
|
|
20
|
+
const HELP_VERSION = new Set([
|
|
21
|
+
'-h',
|
|
22
|
+
'--help',
|
|
23
|
+
'-V',
|
|
24
|
+
'--version',
|
|
25
|
+
'--cli-version',
|
|
26
|
+
'help',
|
|
27
|
+
'version',
|
|
28
|
+
])
|
|
21
29
|
|
|
22
30
|
/** Bare invocation (every() is true for []) or only help/version tokens — skip. */
|
|
23
31
|
function isHelpOrVersion(args: string[]): boolean {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export {
|
|
2
|
+
addUi,
|
|
3
|
+
applyPreset,
|
|
4
|
+
diffUi,
|
|
5
|
+
doctorUi,
|
|
6
|
+
initUi,
|
|
7
|
+
listUi,
|
|
8
|
+
viewUi,
|
|
9
|
+
type InitUiOptions,
|
|
10
|
+
} from '../operations.js'
|
|
11
|
+
export { UI_PRESETS, UiError, type UiLock, type UiPreset } from '../model.js'
|
|
12
|
+
export { discoverUiProject, type UiProject } from '../project.js'
|
|
13
|
+
export { resolveUiRelease } from '../release.js'
|
|
14
|
+
export { shadcnInvocation, type UiRunner } from '../runner.js'
|