@astrale-os/cli 0.8.0-alpha.0 → 0.8.1-alpha.2
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 +4 -1
- package/dist/astrale.js +62968 -41240
- package/dist/public/connect-core.js +55030 -0
- package/dist/public/keys/index.js +45631 -0
- package/dist/public/paths/index.js +43990 -0
- package/dist/types/admin/instance/model.d.ts +40 -0
- package/dist/types/connect-core.d.ts +22 -0
- package/dist/types/connection/auth.d.ts +33 -0
- package/dist/types/errors.d.ts +23 -0
- package/dist/types/identity/registry.d.ts +25 -0
- package/dist/types/keys/algorithm.d.ts +6 -0
- package/dist/types/keys/credential.d.ts +21 -0
- package/dist/types/keys/index.d.ts +4 -0
- package/dist/types/keys/pair.d.ts +34 -0
- package/dist/types/lib/admin-target.d.ts +74 -0
- package/dist/types/lib/ca-fetch.d.ts +2 -0
- package/dist/types/lib/config.d.ts +71 -0
- package/dist/types/lib/format.d.ts +1 -0
- package/dist/types/lib/idp-session.d.ts +33 -0
- package/dist/types/lib/idp.d.ts +397 -0
- package/dist/types/lib/instance-target.d.ts +36 -0
- package/dist/types/lib/instance.d.ts +186 -0
- package/dist/types/lib/log.d.ts +30 -0
- package/dist/types/lib/login-flow.d.ts +49 -0
- package/dist/types/lib/meta.d.ts +36 -0
- package/dist/types/lib/output.d.ts +83 -0
- package/dist/types/lib/table.d.ts +18 -0
- package/dist/types/lib/validation.d.ts +11 -0
- package/dist/types/paths/index.d.ts +2 -0
- package/dist/types/state/exchange-credentials.d.ts +25 -0
- package/dist/types/state/files.d.ts +9 -0
- package/dist/types/state/identities.d.ts +42 -0
- package/dist/types/state/index.d.ts +8 -0
- package/dist/types/state/paths.d.ts +32 -0
- package/package.json +30 -8
- package/src/admin/.spec/api.d.ts +3 -0
- package/src/admin/.spec/architecture.md +15 -0
- package/src/admin/.spec/icon.svg +3 -0
- package/src/admin/.spec/layout.ts +6 -0
- package/src/admin/catalog/.spec/api.d.ts +65 -0
- package/src/admin/catalog/.spec/architecture.md +6 -0
- package/src/admin/catalog/.spec/icon.svg +3 -0
- package/src/admin/catalog/.spec/layout.ts +6 -0
- package/src/admin/catalog/__tests__/client.test.ts +171 -0
- package/src/admin/catalog/client.ts +214 -0
- package/src/admin/catalog/index.ts +13 -0
- package/src/admin/catalog/model.ts +42 -0
- package/src/admin/catalog/tsconfig.json +8 -0
- package/src/admin/graph/.spec/api.d.ts +28 -0
- package/src/admin/graph/.spec/architecture.md +5 -0
- package/src/admin/graph/.spec/icon.svg +3 -0
- package/src/admin/graph/.spec/layout.ts +3 -0
- package/src/admin/graph/index.ts +6 -0
- package/src/admin/graph/nodes.ts +56 -0
- package/src/admin/index.ts +2 -0
- package/src/admin/instance/.spec/api.d.ts +66 -0
- package/src/admin/instance/.spec/architecture.md +10 -0
- package/src/admin/instance/.spec/icon.svg +3 -0
- package/src/admin/instance/.spec/layout.ts +6 -0
- package/src/admin/instance/__tests__/client.test.ts +289 -0
- package/src/admin/instance/client.ts +298 -0
- package/src/admin/instance/index.ts +16 -0
- package/src/admin/instance/model.ts +65 -0
- package/src/admin/instance/tsconfig.json +8 -0
- package/src/commands/__tests__/admin-instance.test.ts +3 -36
- package/src/commands/__tests__/call.test.ts +21 -68
- package/src/commands/__tests__/domain-install-owned.test.ts +26 -27
- package/src/commands/__tests__/domain-list.test.ts +2 -6
- package/src/commands/__tests__/logs.test.ts +50 -98
- package/src/commands/__tests__/ls.test.ts +21 -17
- package/src/commands/__tests__/read-commands.test.ts +171 -101
- package/src/commands/__tests__/view.test.ts +100 -51
- package/src/commands/admin/status.ts +3 -3
- package/src/commands/admin/use.ts +29 -6
- package/src/commands/auth/login.ts +1 -1
- package/src/commands/auth/logout.ts +4 -2
- package/src/commands/auth/status.ts +2 -2
- package/src/commands/auth/token.ts +2 -2
- package/src/commands/browser.ts +1 -1
- package/src/commands/call.ts +55 -91
- package/src/commands/describe.ts +52 -147
- package/src/commands/domain/install.ts +81 -50
- package/src/commands/domain/list.ts +4 -8
- package/src/commands/domain/publish.ts +9 -31
- package/src/commands/get.ts +32 -65
- package/src/commands/identity/__tests__/register.test.ts +93 -0
- package/src/commands/identity/create.ts +2 -2
- package/src/commands/identity/delete.ts +2 -2
- package/src/commands/identity/export.ts +7 -33
- package/src/commands/identity/import.ts +21 -70
- package/src/commands/identity/list.ts +2 -2
- package/src/commands/identity/register.ts +148 -117
- package/src/commands/identity/sync.ts +2 -2
- package/src/commands/identity/unsync.ts +2 -2
- package/src/commands/identity/use.ts +2 -2
- package/src/commands/identity/whoami.ts +2 -2
- package/src/commands/idp/add.ts +1 -1
- package/src/commands/idp/list.ts +1 -1
- package/src/commands/idp/refresh.ts +1 -1
- package/src/commands/idp/remove.ts +2 -2
- package/src/commands/idp/show.ts +1 -1
- package/src/commands/instance/active.ts +2 -2
- package/src/commands/instance/bookmark.ts +8 -2
- package/src/commands/instance/create.ts +1 -1
- package/src/commands/instance/delete.ts +4 -10
- package/src/commands/instance/forget.ts +1 -1
- package/src/commands/instance/list.ts +3 -3
- package/src/commands/instance/status.ts +5 -7
- package/src/commands/instance/use.ts +3 -3
- package/src/commands/logs.ts +152 -279
- package/src/commands/ls.ts +78 -204
- package/src/commands/mutate.ts +70 -139
- package/src/commands/query.ts +100 -278
- package/src/commands/session/analyze.ts +1 -1
- package/src/commands/session/list.ts +1 -1
- package/src/commands/setup.ts +1 -1
- package/src/commands/status.ts +1 -1
- package/src/commands/studio.ts +1 -1
- package/src/commands/token.ts +26 -21
- package/src/commands/update.ts +1 -1
- package/src/commands/use.ts +2 -3
- package/src/commands/view-serve.ts +1 -1
- package/src/commands/view.ts +67 -109
- package/src/connect-core.test.ts +38 -0
- package/src/connect-core.ts +42 -0
- package/src/connection/.spec/api.d.ts +111 -0
- package/src/connection/.spec/architecture.md +41 -0
- package/src/connection/.spec/capabilities/connection.ts +13 -0
- package/src/connection/.spec/examples/call.ts +7 -0
- package/src/connection/.spec/flows/command.ts +20 -0
- package/src/connection/.spec/flows/session.ts +61 -0
- package/src/connection/.spec/laws/connection.ts +181 -0
- package/src/connection/.spec/layout.ts +19 -0
- package/src/{kernel → connection}/__tests__/auth.test.ts +26 -2
- package/src/connection/__tests__/ca-fetch.test.ts +53 -0
- package/src/connection/__tests__/call.test.ts +17 -0
- package/src/connection/__tests__/credential.test.ts +109 -0
- package/src/connection/__tests__/errors.test.ts +96 -0
- package/src/connection/__tests__/exchange.test.ts +224 -0
- package/src/connection/__tests__/fixtures/localhost-cert.pem +19 -0
- package/src/connection/__tests__/fixtures/localhost-key.base64 +1 -0
- package/src/connection/__tests__/managed.test.ts +39 -0
- package/src/connection/__tests__/self.test.ts +51 -0
- package/src/connection/__tests__/session.test.ts +140 -0
- package/src/connection/__tests__/target.test.ts +93 -0
- package/src/{kernel → connection}/auth.ts +8 -8
- package/src/connection/call.ts +7 -0
- package/src/{kernel/run.ts → connection/command.ts} +22 -13
- package/src/connection/credential.ts +114 -0
- package/src/{kernel → connection}/errors.ts +32 -9
- package/src/connection/exchange.ts +274 -0
- package/src/connection/index.ts +8 -0
- package/src/connection/self.ts +120 -0
- package/src/connection/session.ts +176 -0
- package/src/connection/target.ts +111 -0
- package/src/connection/tsconfig.json +6 -0
- package/src/graph/.spec/api.d.ts +34 -0
- package/src/graph/.spec/laws/documents.ts +24 -0
- package/src/graph/.spec/laws/projection.ts +13 -0
- package/src/graph/.spec/layout.ts +6 -0
- package/src/graph/__tests__/mutation.test.ts +43 -0
- package/src/graph/__tests__/projection.test.ts +22 -0
- package/src/graph/__tests__/query.test.ts +154 -0
- package/src/graph/index.ts +3 -0
- package/src/graph/mutation.ts +18 -0
- package/src/graph/projection.ts +21 -0
- package/src/graph/query.ts +108 -0
- package/src/graph/tsconfig.json +6 -0
- package/src/identity/.history/ard/bootstrap-root-transfer.md +57 -0
- package/src/identity/.history/ard/domain-mediated-registration.md +36 -0
- package/src/identity/.spec/api.d.ts +124 -0
- package/src/identity/.spec/architecture.md +33 -0
- package/src/identity/.spec/capabilities/identity.ts +7 -0
- package/src/identity/.spec/flows/bootstrap-root.ts +79 -0
- package/src/identity/.spec/flows/domain-mediated-registration.ts +51 -0
- package/src/identity/.spec/flows/import.ts +29 -0
- package/src/identity/.spec/laws/identity.ts +56 -0
- package/src/identity/.spec/layout.ts +13 -0
- package/src/identity/.spec/schemas/identity-export-v1.schema.json +25 -0
- package/src/identity/__tests__/fixtures/registry-journey.ts +53 -0
- package/src/identity/__tests__/registration.test.ts +64 -0
- package/src/identity/__tests__/registry.test.ts +70 -0
- package/src/identity/__tests__/transfer.test.ts +198 -0
- package/src/identity/index.ts +23 -0
- package/src/identity/registration.ts +72 -0
- package/src/identity/registry.ts +172 -0
- package/src/identity/transfer.ts +187 -0
- package/src/identity/tsconfig.json +6 -0
- package/src/index.ts +1 -0
- package/src/keys/.spec/api.d.ts +74 -0
- package/src/keys/.spec/architecture.md +13 -0
- package/src/keys/.spec/laws/keys.ts +43 -0
- package/src/keys/.spec/layout.ts +6 -0
- package/src/keys/__tests__/algorithm.test.ts +39 -0
- package/src/keys/__tests__/keys.test.ts +168 -0
- package/src/keys/algorithm.ts +33 -0
- package/src/keys/credential.ts +112 -0
- package/src/keys/index.ts +14 -0
- package/src/keys/pair.ts +219 -0
- package/src/keys/tsconfig.json +6 -0
- package/src/lib/__tests__/admin-target.test.ts +67 -7
- package/src/lib/__tests__/binary.test.ts +2 -3
- package/src/lib/__tests__/command-dx.test.ts +2 -2
- package/src/lib/__tests__/config.test.ts +3 -2
- package/src/lib/__tests__/idp.test.ts +4 -6
- package/src/lib/__tests__/instance-target.test.ts +7 -4
- package/src/lib/__tests__/local-status.test.ts +1 -1
- package/src/lib/__tests__/log.test.ts +31 -0
- package/src/lib/__tests__/meta.test.ts +55 -0
- package/src/lib/__tests__/sdk-deps.test.ts +1 -1
- package/src/lib/__tests__/self.test.ts +8 -230
- package/src/lib/__tests__/use-target.test.ts +1 -1
- package/src/lib/__tests__/view-open-intent.test.ts +78 -104
- package/src/lib/__tests__/view-session.test.ts +99 -0
- package/src/lib/admin-domain.ts +39 -25
- package/src/lib/admin-instance.ts +38 -43
- package/src/lib/admin-target.ts +67 -13
- package/src/lib/binary.ts +13 -34
- package/src/lib/browser.ts +1 -1
- package/src/{kernel → lib}/ca-fetch.ts +35 -27
- package/src/lib/config.ts +1 -1
- package/src/lib/idp-session.ts +1 -1
- package/src/lib/idp.ts +3 -8
- package/src/lib/instance-target.ts +12 -8
- package/src/lib/instance.ts +34 -2
- package/src/lib/local-status.ts +2 -2
- package/src/lib/log.ts +3 -1
- package/src/lib/login-flow.ts +32 -5
- package/src/lib/meta.ts +15 -11
- package/src/lib/provision-instance.ts +4 -12
- package/src/lib/self.ts +5 -147
- package/src/lib/update.ts +1 -1
- package/src/lib/use-target.ts +1 -1
- package/src/lib/validation.ts +2 -2
- package/src/lib/view/open-intent.ts +7 -37
- package/src/lib/view/port-allocation.ts +1 -1
- package/src/lib/view/resolve.ts +52 -45
- package/src/lib/view/server.ts +16 -33
- package/src/lib/view/session.ts +51 -22
- package/src/paths/.spec/api.d.ts +13 -0
- package/src/paths/.spec/architecture.md +5 -0
- package/src/paths/.spec/layout.ts +3 -0
- package/src/paths/index.ts +13 -0
- package/src/program/.spec/api.d.ts +42 -0
- package/src/program/.spec/architecture.md +18 -0
- package/src/program/.spec/laws/program.ts +29 -0
- package/src/program/.spec/layout.ts +14 -0
- package/src/program/__tests__/program.test.ts +359 -0
- package/src/program/build.ts +190 -0
- package/src/program/command.ts +42 -0
- package/src/program/index.ts +2 -0
- package/src/program/options.ts +40 -0
- package/src/{registry.ts → program/registry.ts} +5 -1
- package/src/program/tsconfig.json +6 -0
- package/src/setup/steps/admin.ts +18 -3
- package/src/setup/steps/instance.ts +1 -1
- package/src/state/.spec/api.d.ts +133 -0
- package/src/state/.spec/architecture.md +31 -0
- package/src/state/.spec/capabilities/state.ts +7 -0
- package/src/state/.spec/flows/identity-update.ts +22 -0
- package/src/state/.spec/laws/state.ts +73 -0
- package/src/state/.spec/layout.ts +14 -0
- package/src/state/__tests__/exchange-credentials.test.ts +188 -0
- package/src/state/__tests__/files.test.ts +113 -0
- package/src/state/__tests__/fixtures/identity-transition.ts +37 -0
- package/src/state/__tests__/identities.test.ts +142 -0
- package/src/state/__tests__/paths.test.ts +27 -0
- package/src/state/exchange-credentials.ts +219 -0
- package/src/state/files.ts +129 -0
- package/src/state/identities.ts +236 -0
- package/src/state/index.ts +29 -0
- package/src/state/paths.ts +61 -0
- package/src/state/tsconfig.json +6 -0
- package/src/telemetry/__tests__/redact.test.ts +3 -3
- package/src/telemetry/__tests__/trigger.test.ts +1 -1
- package/src/telemetry/settings.ts +1 -1
- package/src/telemetry/store.ts +1 -1
- package/src/test-utils.ts +1 -1
- package/studio/client/dist/assets/elk-api-D0cBetPW.js +1 -0
- package/studio/client/dist/assets/elk-worker.min-C9JGDOE-.js +6312 -0
- package/studio/client/dist/assets/index-BQnJ5sgd.css +1 -0
- package/studio/client/dist/assets/index-ChOr3yP0.css +1 -0
- package/studio/client/dist/assets/index-Dspir4w7.js +81 -0
- package/studio/client/dist/assets/index-bVD2KJgz.js +8 -0
- package/studio/client/dist/index.html +2 -2
- package/studio/package.json +10 -1
- package/studio/server/introspect/extractor.ts +1 -1
- package/studio/server/state/views.test.ts +3 -7
- package/studio/server/state/views.ts +23 -30
- package/studio/tsconfig.json +23 -0
- package/tsconfig.json +1 -1
- package/viewer/dist/main.js +65 -39
- package/viewer/tsconfig.json +13 -0
- package/src/command.ts +0 -42
- package/src/commands/__tests__/help-contract.test.ts +0 -164
- package/src/kernel/__tests__/client-owned-lookup.test.ts +0 -51
- package/src/kernel/__tests__/errors.test.ts +0 -43
- package/src/kernel/__tests__/expand.test.ts +0 -123
- package/src/kernel/client.ts +0 -214
- package/src/kernel/expand.ts +0 -192
- package/src/kernel/graph.ts +0 -96
- package/src/kernel/index.ts +0 -29
- package/src/kernel/options.ts +0 -22
- package/src/kernel/types.ts +0 -14
- package/src/lib/__tests__/domain-identity.test.ts +0 -60
- package/src/lib/__tests__/fs-atomic.test.ts +0 -104
- package/src/lib/__tests__/identity.test.ts +0 -79
- package/src/lib/__tests__/keys.test.ts +0 -129
- package/src/lib/domain-identity.ts +0 -49
- package/src/lib/env.ts +0 -49
- package/src/lib/fs-atomic.ts +0 -126
- package/src/lib/identity.ts +0 -256
- package/src/lib/keys.ts +0 -294
- package/src/lib/paths.ts +0 -11
- package/src/program.ts +0 -213
- package/studio/client/dist/assets/index-BaqEuIQJ.js +0 -109
- package/studio/client/dist/assets/index-jRdExahh.css +0 -1
package/src/lib/view/resolve.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ResolvedView as SessionResolvedView } from '@astrale-os/kernel-client/session'
|
|
2
|
+
import type { ResolvedView } from '@astrale-os/shell'
|
|
3
|
+
|
|
4
|
+
import { Path } from '@astrale-os/sdk/graph/path'
|
|
5
|
+
|
|
6
|
+
import type { ConnectionContext } from '../../connection'
|
|
2
7
|
|
|
3
8
|
import { AstraleError } from '../../errors'
|
|
4
9
|
|
|
@@ -9,7 +14,6 @@ import { AstraleError } from '../../errors'
|
|
|
9
14
|
* it via `view_for`.
|
|
10
15
|
*/
|
|
11
16
|
|
|
12
|
-
const VIEW_RESOLVE_PATH = '/:kernel.astrale.ai:class.View:resolve'
|
|
13
17
|
const VIEW_PATH_RE = /^\/:[^\s/:@]+:view\.[a-z][a-z0-9-]*$/
|
|
14
18
|
|
|
15
19
|
export type ViewSpec = { kind: 'view' | 'target'; path: string }
|
|
@@ -23,31 +27,62 @@ export function parseViewSpec(spec: string): ViewSpec {
|
|
|
23
27
|
)
|
|
24
28
|
}
|
|
25
29
|
|
|
26
|
-
/**
|
|
27
|
-
export
|
|
30
|
+
/** The owning Domain principal used when an explicit ViewPath has no target override. */
|
|
31
|
+
export function viewOwnerTarget(viewPath: string): string {
|
|
32
|
+
const parsed = Path.parse(viewPath)
|
|
33
|
+
if (parsed.ast.anchor.kind !== 'domain') {
|
|
34
|
+
throw new AstraleError('INVALID_ARGUMENT', `ViewPath has no Domain owner: ${viewPath}`)
|
|
35
|
+
}
|
|
36
|
+
return `/:${parsed.ast.anchor.origin}`
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** One exact Shell selection plus stable presentation fields for CLI output. */
|
|
40
|
+
export type ViewCandidate = ResolvedView & {
|
|
28
41
|
id: string
|
|
29
42
|
path: string
|
|
30
43
|
url: string
|
|
31
44
|
name?: string
|
|
32
|
-
handshake
|
|
45
|
+
handshake: 'shell' | 'none'
|
|
33
46
|
origin: 'self' | 'class'
|
|
47
|
+
issuer: string
|
|
48
|
+
etag: string
|
|
49
|
+
revision: string
|
|
34
50
|
}
|
|
35
51
|
|
|
36
52
|
export async function resolveViewCandidates(
|
|
37
|
-
ctx:
|
|
53
|
+
ctx: ConnectionContext,
|
|
38
54
|
nodePath: string,
|
|
39
55
|
): Promise<ViewCandidate[]> {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
56
|
+
const target = Path.parse(nodePath).raw
|
|
57
|
+
const catalog = await ctx.session.viewsFor(target)
|
|
58
|
+
return catalog.views.map((route) => toCandidate(target, route))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function toCandidate(target: ResolvedView['target'], route: SessionResolvedView): ViewCandidate {
|
|
62
|
+
const key = String(route.key)
|
|
63
|
+
return Object.freeze({
|
|
64
|
+
target,
|
|
65
|
+
route,
|
|
66
|
+
id: key,
|
|
67
|
+
path: `/:${key}`,
|
|
68
|
+
url: route.href,
|
|
69
|
+
name: key.slice(key.lastIndexOf(':view.') + ':view.'.length),
|
|
70
|
+
handshake: route.handshake,
|
|
71
|
+
origin: route.declaration.target.kind === 'domain' ? 'self' : 'class',
|
|
72
|
+
issuer: route.issuer,
|
|
73
|
+
etag: route.etag,
|
|
74
|
+
revision: route.revision,
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Strip presentation aliases before crossing the Shell mount boundary. */
|
|
79
|
+
export function selectedView(candidate: ViewCandidate): ResolvedView {
|
|
80
|
+
return Object.freeze({ target: candidate.target, route: candidate.route })
|
|
45
81
|
}
|
|
46
82
|
|
|
47
83
|
/** The slug tail of a candidate: `view.dashboard` → `dashboard`. */
|
|
48
84
|
export function candidateSlug(candidate: ViewCandidate): string {
|
|
49
|
-
|
|
50
|
-
return candidate.name ?? fromPath
|
|
85
|
+
return candidate.name ?? candidate.id.slice(candidate.id.lastIndexOf(':view.') + ':view.'.length)
|
|
51
86
|
}
|
|
52
87
|
|
|
53
88
|
export function pickCandidate(
|
|
@@ -56,7 +91,10 @@ export function pickCandidate(
|
|
|
56
91
|
slug?: string,
|
|
57
92
|
): ViewCandidate | 'ambiguous' {
|
|
58
93
|
if (slug) {
|
|
59
|
-
const match = candidates.find(
|
|
94
|
+
const match = candidates.find(
|
|
95
|
+
(candidate) =>
|
|
96
|
+
candidate.id === slug || candidate.path === slug || candidateSlug(candidate) === slug,
|
|
97
|
+
)
|
|
60
98
|
if (!match) {
|
|
61
99
|
throw new AstraleError(
|
|
62
100
|
'VIEW_NOT_FOUND',
|
|
@@ -71,34 +109,3 @@ export function pickCandidate(
|
|
|
71
109
|
if (candidates.length === 1) return candidates[0]
|
|
72
110
|
return 'ambiguous'
|
|
73
111
|
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Apply a `--view-url` override: an origin-only value swaps the origin and
|
|
77
|
-
* keeps the resolved path, a value with a path replaces the URL wholesale.
|
|
78
|
-
*/
|
|
79
|
-
export function applyViewUrlOverride(resolvedUrl: string, override: string): string {
|
|
80
|
-
const parsed = parseUrl(override)
|
|
81
|
-
if (parsed.pathname !== '/') return parsed.toString()
|
|
82
|
-
const original = parseUrl(resolvedUrl)
|
|
83
|
-
return new URL(original.pathname + original.search + original.hash, parsed.origin).toString()
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* The kernel addresses locally-served workers as `host.docker.internal`
|
|
88
|
-
* (reachable from its container); the host browser reaches the same worker on
|
|
89
|
-
* loopback. Rewrite so the iframe loads without an /etc/hosts entry.
|
|
90
|
-
*/
|
|
91
|
-
export function rewriteLocalViewUrl(url: string): string {
|
|
92
|
-
const parsed = parseUrl(url)
|
|
93
|
-
if (parsed.hostname !== 'host.docker.internal') return url
|
|
94
|
-
parsed.hostname = '127.0.0.1'
|
|
95
|
-
return parsed.toString()
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function parseUrl(raw: string): URL {
|
|
99
|
-
try {
|
|
100
|
-
return new URL(raw)
|
|
101
|
-
} catch {
|
|
102
|
-
throw new AstraleError('INVALID_URL', `Not a valid URL: ${raw}`)
|
|
103
|
-
}
|
|
104
|
-
}
|
package/src/lib/view/server.ts
CHANGED
|
@@ -7,8 +7,8 @@ import { Readable } from 'node:stream'
|
|
|
7
7
|
|
|
8
8
|
import type { ViewServeConfig } from './session'
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import { fetchWithCaFile } from '
|
|
10
|
+
import { withClientSession } from '../../connection'
|
|
11
|
+
import { fetchWithCaFile } from '../ca-fetch'
|
|
12
12
|
import { viewerDistDir } from './assets'
|
|
13
13
|
import { removeSessionFiles } from './session'
|
|
14
14
|
|
|
@@ -29,7 +29,7 @@ const IDLE_SWEEP_MS = 60_000
|
|
|
29
29
|
|
|
30
30
|
export type PageStatus = { state: string; error?: string; at: string }
|
|
31
31
|
|
|
32
|
-
type TokenGrant = { token: string; expiresAt: number; kind: 'minted'
|
|
32
|
+
type TokenGrant = { token: string; expiresAt: number; kind: 'minted' }
|
|
33
33
|
|
|
34
34
|
export function startViewServer(config: ViewServeConfig): Server {
|
|
35
35
|
const { session, proxy } = config
|
|
@@ -40,31 +40,15 @@ export function startViewServer(config: ViewServeConfig): Server {
|
|
|
40
40
|
let grant: TokenGrant | null = null
|
|
41
41
|
let lastActivity = Date.now()
|
|
42
42
|
|
|
43
|
-
/**
|
|
44
|
-
* Prefer a kernel-minted TTL-bound identity credential (what the GUI hands
|
|
45
|
-
* its iframes); fall back to the raw CLI credential so the session works
|
|
46
|
-
* anywhere the CLI itself can call.
|
|
47
|
-
*/
|
|
43
|
+
/** Mint one TTL-bound credential; raw CLI credentials never enter the browser session. */
|
|
48
44
|
async function freshGrant(): Promise<TokenGrant> {
|
|
49
45
|
if (grant && grant.expiresAt - Date.now() > TOKEN_REFRESH_MARGIN_MS) return grant
|
|
50
|
-
grant = await
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
kind: 'minted' as const,
|
|
57
|
-
}
|
|
58
|
-
} catch (error) {
|
|
59
|
-
console.log(
|
|
60
|
-
`mint failed (${error instanceof Error ? error.message : String(error)}) — falling back to the raw CLI credential`,
|
|
61
|
-
)
|
|
62
|
-
const token = ctx.credential
|
|
63
|
-
return {
|
|
64
|
-
token,
|
|
65
|
-
expiresAt: jwtExpiry(token) ?? Date.now() + FALLBACK_TOKEN_TTL_MS,
|
|
66
|
-
kind: 'raw' as const,
|
|
67
|
-
}
|
|
46
|
+
grant = await withClientSession(config.kernel, async ({ auth, target }) => {
|
|
47
|
+
const token = await auth.mint({ audience: target.kernelIssuer, ttlSeconds: 3_600 })
|
|
48
|
+
return {
|
|
49
|
+
token,
|
|
50
|
+
expiresAt: jwtExpiry(token) ?? Date.now() + FALLBACK_TOKEN_TTL_MS,
|
|
51
|
+
kind: 'minted' as const,
|
|
68
52
|
}
|
|
69
53
|
})
|
|
70
54
|
return grant
|
|
@@ -108,14 +92,9 @@ export function startViewServer(config: ViewServeConfig): Server {
|
|
|
108
92
|
}
|
|
109
93
|
if (sub === '/config.json' && req.method === 'GET') {
|
|
110
94
|
json(res, 200, {
|
|
111
|
-
|
|
112
|
-
viewPath: session.view.path ?? null,
|
|
113
|
-
viewName: session.view.name ?? null,
|
|
114
|
-
functionId: session.view.functionId,
|
|
115
|
-
handshake: session.view.handshake,
|
|
116
|
-
targetNodeId: session.target?.id ?? null,
|
|
117
|
-
targetPath: session.target?.path ?? null,
|
|
95
|
+
view: session.view,
|
|
118
96
|
kernelUrl: proxy.direct ? proxy.kernelUrl : `${base}/k`,
|
|
97
|
+
kernelIssuer: proxy.issuer,
|
|
119
98
|
identity: session.identity ?? null,
|
|
120
99
|
instance: session.instance ?? null,
|
|
121
100
|
sessionId: session.id,
|
|
@@ -123,6 +102,10 @@ export function startViewServer(config: ViewServeConfig): Server {
|
|
|
123
102
|
return
|
|
124
103
|
}
|
|
125
104
|
if (sub === '/token' && req.method === 'POST') {
|
|
105
|
+
if (session.view.route.handshake !== 'shell') {
|
|
106
|
+
json(res, 403, { error: 'plain views have no Astrale credential privilege' })
|
|
107
|
+
return
|
|
108
|
+
}
|
|
126
109
|
const fresh = await freshGrant()
|
|
127
110
|
json(res, 200, { token: fresh.token, expiresAt: fresh.expiresAt, kind: fresh.kind })
|
|
128
111
|
return
|
package/src/lib/view/session.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ResolvedView } from '@astrale-os/shell'
|
|
2
|
+
|
|
3
|
+
import { closeSync, fchmodSync, openSync } from 'node:fs'
|
|
4
|
+
import { chmod, mkdir, readdir, readFile, rm } from 'node:fs/promises'
|
|
2
5
|
import { join } from 'node:path'
|
|
3
6
|
|
|
4
|
-
import { paths } from '
|
|
7
|
+
import { atomicWrite, paths } from '../../state/index'
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* View-session records: one detached server process per open view, tracked as
|
|
@@ -18,15 +21,8 @@ export type ViewSessionRecord = {
|
|
|
18
21
|
nonce: string
|
|
19
22
|
/** Host page URL, nonce-scoped: `http://127.0.0.1:<port>/s/<nonce>/`. */
|
|
20
23
|
pageUrl: string
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
functionId: string
|
|
24
|
-
handshake: 'shell' | 'none'
|
|
25
|
-
/** ViewPath or resolved view node path, when the view came from the graph. */
|
|
26
|
-
path?: string
|
|
27
|
-
name?: string
|
|
28
|
-
}
|
|
29
|
-
target?: { id?: string; path?: string }
|
|
24
|
+
/** Exact verified View placement passed to Shell without split aliases. */
|
|
25
|
+
view: ResolvedView
|
|
30
26
|
instance?: string
|
|
31
27
|
identity?: string
|
|
32
28
|
createdAt: string
|
|
@@ -44,24 +40,57 @@ export type ViewServeConfig = {
|
|
|
44
40
|
* loopback proxy). Otherwise the child gets the nonce-scoped proxy, which
|
|
45
41
|
* handles CORS and self-signed local CAs.
|
|
46
42
|
*/
|
|
47
|
-
proxy: { kernelUrl: string; caFile?: string; direct: boolean }
|
|
43
|
+
proxy: { kernelUrl: string; issuer: string; caFile?: string; direct: boolean }
|
|
48
44
|
idleMs: number
|
|
49
45
|
}
|
|
50
46
|
|
|
51
|
-
export const recordPath = (id: string): string =>
|
|
52
|
-
|
|
53
|
-
export const
|
|
47
|
+
export const recordPath = (id: string, directory = VIEW_DIR): string =>
|
|
48
|
+
join(directory, `${id}.json`)
|
|
49
|
+
export const logPath = (id: string, directory = VIEW_DIR): string => join(directory, `${id}.log`)
|
|
50
|
+
export const configPath = (id: string, directory = VIEW_DIR): string =>
|
|
51
|
+
join(directory, `${id}.config.json`)
|
|
52
|
+
|
|
53
|
+
/** Create or repair the credential-bearing session directory as owner-only. */
|
|
54
|
+
export async function ensureViewDirectory(directory = VIEW_DIR): Promise<void> {
|
|
55
|
+
await mkdir(directory, { recursive: true, mode: 0o700 })
|
|
56
|
+
await chmod(directory, 0o700)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function saveRecord(record: ViewSessionRecord, directory = VIEW_DIR): Promise<void> {
|
|
60
|
+
await ensureViewDirectory(directory)
|
|
61
|
+
await atomicWrite(recordPath(record.id, directory), `${JSON.stringify(record, null, 2)}\n`)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Atomically publish the detached process config; it may contain raw `--creds`. */
|
|
65
|
+
export async function saveServeConfig(
|
|
66
|
+
config: ViewServeConfig,
|
|
67
|
+
directory = VIEW_DIR,
|
|
68
|
+
): Promise<void> {
|
|
69
|
+
await ensureViewDirectory(directory)
|
|
70
|
+
await atomicWrite(
|
|
71
|
+
configPath(config.session.id, directory),
|
|
72
|
+
`${JSON.stringify(config, null, 2)}\n`,
|
|
73
|
+
)
|
|
74
|
+
}
|
|
54
75
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
await
|
|
76
|
+
/** Open or repair a session log as owner-only and return its descriptor. */
|
|
77
|
+
export async function openSessionLog(id: string, directory = VIEW_DIR): Promise<number> {
|
|
78
|
+
await ensureViewDirectory(directory)
|
|
79
|
+
const descriptor = openSync(logPath(id, directory), 'a', 0o600)
|
|
80
|
+
try {
|
|
81
|
+
fchmodSync(descriptor, 0o600)
|
|
82
|
+
return descriptor
|
|
83
|
+
} catch (error) {
|
|
84
|
+
closeSync(descriptor)
|
|
85
|
+
throw error
|
|
86
|
+
}
|
|
58
87
|
}
|
|
59
88
|
|
|
60
|
-
export async function removeSessionFiles(id: string): Promise<void> {
|
|
89
|
+
export async function removeSessionFiles(id: string, directory = VIEW_DIR): Promise<void> {
|
|
61
90
|
await Promise.all([
|
|
62
|
-
rm(recordPath(id), { force: true }),
|
|
63
|
-
rm(configPath(id), { force: true }),
|
|
64
|
-
rm(logPath(id), { force: true }),
|
|
91
|
+
rm(recordPath(id, directory), { force: true }),
|
|
92
|
+
rm(configPath(id, directory), { force: true }),
|
|
93
|
+
rm(logPath(id, directory), { force: true }),
|
|
65
94
|
])
|
|
66
95
|
}
|
|
67
96
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export {
|
|
2
|
+
ASTRALE_HOME,
|
|
3
|
+
CONFIG_PATH,
|
|
4
|
+
DATA_DIR,
|
|
5
|
+
IDENTITIES_PATH,
|
|
6
|
+
IDPS_PATH,
|
|
7
|
+
IDP_SESSIONS_DIR,
|
|
8
|
+
INSTALL_PATH,
|
|
9
|
+
INSTANCES_PATH,
|
|
10
|
+
KEYS_DIR,
|
|
11
|
+
createPaths,
|
|
12
|
+
} from '../../state/.spec/api.js'
|
|
13
|
+
export type { PathEnvironment, Paths } from '../../state/.spec/api.js'
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export {
|
|
2
|
+
ASTRALE_HOME,
|
|
3
|
+
CONFIG_PATH,
|
|
4
|
+
DATA_DIR,
|
|
5
|
+
IDENTITIES_PATH,
|
|
6
|
+
IDPS_PATH,
|
|
7
|
+
IDP_SESSIONS_DIR,
|
|
8
|
+
INSTALL_PATH,
|
|
9
|
+
INSTANCES_PATH,
|
|
10
|
+
KEYS_DIR,
|
|
11
|
+
createPaths,
|
|
12
|
+
} from '../state/index'
|
|
13
|
+
export type { PathEnvironment, Paths } from '../state/index'
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Command } from 'commander'
|
|
2
|
+
|
|
3
|
+
/** One positional argument in a CLI command definition. */
|
|
4
|
+
export interface CommandArgument {
|
|
5
|
+
readonly name: string
|
|
6
|
+
readonly description: string
|
|
7
|
+
readonly required?: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** One Commander-compatible option in a CLI command definition. */
|
|
11
|
+
export interface CommandOption {
|
|
12
|
+
readonly flags: string
|
|
13
|
+
readonly description: string
|
|
14
|
+
readonly default?: string
|
|
15
|
+
readonly choices?: string[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Internal authoring shape implemented by each command module. */
|
|
19
|
+
export interface CommandDefinition {
|
|
20
|
+
readonly name: string
|
|
21
|
+
readonly description: string
|
|
22
|
+
readonly summary?: string
|
|
23
|
+
readonly hidden?: boolean
|
|
24
|
+
readonly aliases?: string[]
|
|
25
|
+
readonly arguments?: CommandArgument[]
|
|
26
|
+
readonly options?: CommandOption[]
|
|
27
|
+
readonly afterHelpText?: string
|
|
28
|
+
/** Opaque Commander callback; each command module owns its concrete argument tuple. */
|
|
29
|
+
readonly action: (...args: never[]) => Promise<void>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Internal composition shape for one group of commands. */
|
|
33
|
+
export interface CommandGroup {
|
|
34
|
+
readonly name: string
|
|
35
|
+
readonly description: string
|
|
36
|
+
readonly summary?: string
|
|
37
|
+
readonly commands: CommandDefinition[]
|
|
38
|
+
readonly subgroups?: CommandGroup[]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Build a fresh, complete Commander tree without parsing process arguments. */
|
|
42
|
+
export function buildProgram(): Promise<Command>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# CLI program architecture
|
|
2
|
+
|
|
3
|
+
The program module owns Commander composition, not command effects or process lifecycle. It loads
|
|
4
|
+
the existing command definitions only when `buildProgram` runs, attaches the single shared Kernel
|
|
5
|
+
option set, and returns a fresh unparsed tree. Command modules own their actions; the binary owns
|
|
6
|
+
argv, telemetry, signal, error, and exit handling.
|
|
7
|
+
|
|
8
|
+
```mermaid
|
|
9
|
+
flowchart LR
|
|
10
|
+
B[Binary entrypoint] --> P[buildProgram]
|
|
11
|
+
D[Command definitions] --> P
|
|
12
|
+
O[Shared Kernel options] --> P
|
|
13
|
+
P --> C[Fresh Commander tree]
|
|
14
|
+
C --> B
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The module is private to the CLI package. The exhaustive user-facing grammar remains the generated
|
|
18
|
+
Commander inventory rather than a duplicate declaration tree.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { defineLaw } from '@astrale-os/spec/authoring'
|
|
2
|
+
|
|
3
|
+
export const CLI_PROGRAM_LEDGERED_SURFACE = defineLaw({
|
|
4
|
+
id: 'CLI-PROGRAM-LEDGERED-SURFACE',
|
|
5
|
+
statement:
|
|
6
|
+
'Program composition exposes one exact root and nested command surface; metadata, options, aliases, visibility, Commander behavior, rendered help, and package-derived version change only through a deliberate compatibility-ledger entry.',
|
|
7
|
+
tests: [
|
|
8
|
+
{
|
|
9
|
+
file: '__tests__/program.test.ts',
|
|
10
|
+
id: 'TEST-CLI-PROGRAM-MATCHES-LEDGERED-SURFACE',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
file: '__tests__/program.test.ts',
|
|
14
|
+
id: 'TEST-CLI-PROGRAM-VERSION-SINGLE-SOURCE',
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export const CLI_PROGRAM_FRESH_ROOT = defineLaw({
|
|
20
|
+
id: 'CLI-PROGRAM-FRESH-ROOT',
|
|
21
|
+
statement:
|
|
22
|
+
'Each build produces an independent unparsed Commander root; mutation by one consumer cannot alter a later build.',
|
|
23
|
+
tests: [
|
|
24
|
+
{
|
|
25
|
+
file: '__tests__/program.test.ts',
|
|
26
|
+
id: 'TEST-CLI-PROGRAM-BUILDS-ISOLATED-ROOTS',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
})
|