@astrale-os/cli 0.8.1-alpha.7 → 1.0.0-beta.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 +15 -3
- package/dist/astrale.js +86 -35
- package/package.json +5 -5
- package/src/commands/__tests__/domain-install-operation.test.ts +121 -0
- package/src/commands/__tests__/domain-install-owned.test.ts +66 -0
- package/src/commands/__tests__/install-direct.test.ts +3 -2
- package/src/commands/domain/install.ts +78 -15
- package/src/connection/__tests__/errors.test.ts +82 -6
- package/src/connection/command.ts +9 -1
- package/src/connection/errors.ts +19 -8
- package/src/connection/index.ts +1 -1
- package/src/connection/reasons.ts +26 -12
- package/src/program/__tests__/program.test.ts +1 -1
- package/studio/client/dist/assets/{elk-api-D0cBetPW.js → elk-api-D2xgMJvi.js} +1 -1
- package/studio/client/dist/assets/{index-BQnJ5sgd.css → index-BMdnsIJA.css} +1 -1
- package/studio/client/dist/assets/index-D-vRV8w7.js +8 -0
- package/studio/client/dist/assets/index-LGSWRrk8.js +81 -0
- package/studio/client/dist/index.html +2 -2
- package/studio/package.json +1 -1
- package/studio/server/agent/prompts/anchors.test.ts +74 -0
- package/studio/server/agent/prompts/anchors.ts +85 -15
- package/studio/server/agent/prompts/system.test.ts +12 -0
- package/studio/server/agent/prompts/system.ts +6 -6
- package/studio/server/api.ts +1 -5
- package/studio/server/cache.ts +5 -2
- package/studio/server/domain.test.ts +67 -0
- package/studio/server/domain.ts +29 -6
- package/studio/server/index.ts +1 -3
- package/studio/server/introspect/anatomy-extras.test.ts +104 -1
- package/studio/server/introspect/anatomy-extras.ts +340 -8
- package/studio/server/introspect/anatomy.test.ts +33 -0
- package/studio/server/introspect/anatomy.ts +22 -7
- package/studio/server/introspect/bundle.ts +7 -1
- package/studio/server/introspect/canonical-schema.test.ts +395 -0
- package/studio/server/introspect/canonical-schema.ts +751 -0
- package/studio/server/introspect/core-extractor.ts +30 -10
- package/studio/server/introspect/core.ts +4 -2
- package/studio/server/introspect/diff.test.ts +124 -0
- package/studio/server/introspect/diff.ts +252 -23
- package/studio/server/introspect/extractor.ts +46 -14
- package/studio/server/introspect/overlay-tsmorph.test.ts +164 -1
- package/studio/server/introspect/overlay-tsmorph.ts +381 -106
- package/studio/server/introspect/overlay.test.ts +72 -0
- package/studio/server/introspect/overlay.ts +16 -6
- package/studio/server/introspect/runtime.test.ts +217 -0
- package/studio/server/introspect/runtime.ts +18 -6
- package/studio/server/introspect/schema-refs.test.ts +100 -0
- package/studio/server/introspect/schema-refs.ts +23 -2
- package/studio/server/state/baseline.test.ts +51 -0
- package/studio/server/state/baseline.ts +31 -2
- package/studio/server/state/create.test.ts +37 -0
- package/studio/server/state/create.ts +21 -15
- package/studio/server/state/instance.test.ts +63 -0
- package/studio/server/state/instance.ts +65 -29
- package/studio/server/state/views.test.ts +209 -9
- package/studio/server/state/views.ts +176 -69
- package/studio/server/watch.test.ts +36 -0
- package/studio/server/watch.ts +24 -16
- package/studio/server/workspace-watch.ts +8 -3
- package/studio/shared/types.ts +164 -33
- package/studio/client/dist/assets/index-Dspir4w7.js +0 -81
- package/studio/client/dist/assets/index-bVD2KJgz.js +0 -8
- package/studio/server/view-dev-server.test.ts +0 -111
- package/studio/server/view-dev-server.ts +0 -372
|
@@ -6,6 +6,7 @@ import type { KernelCommandOpts } from '../../connection'
|
|
|
6
6
|
import type { CommandDefinition } from '../../program/index'
|
|
7
7
|
|
|
8
8
|
import { createPathCall, runKernelCommand, withAdminClientSession } from '../../connection'
|
|
9
|
+
import { formatKernelError } from '../../connection/errors'
|
|
9
10
|
import { AstraleError } from '../../errors'
|
|
10
11
|
import {
|
|
11
12
|
installAdminDomainInContext,
|
|
@@ -21,11 +22,7 @@ import { confirmWithInput, promptText, selectFrom } from '../../lib/prompt'
|
|
|
21
22
|
import { isHttpUrl } from '../../lib/validation'
|
|
22
23
|
|
|
23
24
|
/** Public Kernel install syscall input for one remote URL. */
|
|
24
|
-
export function directInstallCallInput(
|
|
25
|
-
url: string,
|
|
26
|
-
token?: string,
|
|
27
|
-
operation: string = crypto.randomUUID(),
|
|
28
|
-
) {
|
|
25
|
+
export function directInstallCallInput(url: string, operation: string, token?: string) {
|
|
29
26
|
return Object.freeze({
|
|
30
27
|
operation,
|
|
31
28
|
domains: [
|
|
@@ -50,9 +47,28 @@ type DirectInstallResult = {
|
|
|
50
47
|
}[]
|
|
51
48
|
}
|
|
52
49
|
|
|
50
|
+
const OPERATION_ID_PATTERN =
|
|
51
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u
|
|
52
|
+
|
|
53
|
+
function acceptOperationId(input: unknown): string {
|
|
54
|
+
if (typeof input !== 'string' || !OPERATION_ID_PATTERN.test(input)) {
|
|
55
|
+
throw new AstraleError(
|
|
56
|
+
'INVALID_FLAG',
|
|
57
|
+
'--operation must be a canonical lowercase UUIDv4.',
|
|
58
|
+
'Omit --operation for a fresh install; use it only with the exact UUID printed for recovery.',
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
return input
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function createOperationId(): string {
|
|
65
|
+
return acceptOperationId(globalThis.crypto.randomUUID())
|
|
66
|
+
}
|
|
67
|
+
|
|
53
68
|
type InstallOpts = KernelCommandOpts &
|
|
54
69
|
AdminTargetCommandOpts & {
|
|
55
70
|
direct?: boolean
|
|
71
|
+
operation?: string
|
|
56
72
|
token?: string
|
|
57
73
|
allowIdentityOverride?: boolean
|
|
58
74
|
// Global flags (program.ts) that force non-interactive.
|
|
@@ -78,6 +94,10 @@ Behavior:
|
|
|
78
94
|
origin differs from its serving host, it requires explicit consent (an
|
|
79
95
|
interactive DANGER prompt, or --allow-identity-override in scripts).
|
|
80
96
|
|
|
97
|
+
A fresh, strong operation id is generated automatically. Use --operation
|
|
98
|
+
only to retry or recover the exact same direct install after an outcome-unknown
|
|
99
|
+
timeout or disconnect.
|
|
100
|
+
|
|
81
101
|
Examples:
|
|
82
102
|
$ astrale domain install crm.acme.dev -i staging # by origin, via admin
|
|
83
103
|
$ astrale domain install https://crm.acme.dev # by url, via admin
|
|
@@ -103,12 +123,26 @@ Examples:
|
|
|
103
123
|
flags: '--token <token>',
|
|
104
124
|
description: 'Bearer token for private domain install endpoints (--direct only)',
|
|
105
125
|
},
|
|
126
|
+
{
|
|
127
|
+
flags: '--operation <uuid>',
|
|
128
|
+
description: 'Reuse an exact direct-install operation id for explicit retry/recovery',
|
|
129
|
+
},
|
|
106
130
|
{
|
|
107
131
|
flags: '--allow-identity-override',
|
|
108
132
|
description: 'Consent to a domain whose origin differs from its serving host (--direct only)',
|
|
109
133
|
},
|
|
110
134
|
],
|
|
111
135
|
action: async (target: string | undefined, opts: InstallOpts) => {
|
|
136
|
+
if (opts.operation !== undefined && !opts.direct) {
|
|
137
|
+
fatal(
|
|
138
|
+
new AstraleError(
|
|
139
|
+
'INVALID_FLAG',
|
|
140
|
+
'--operation is valid only with --direct.',
|
|
141
|
+
'Ordinary direct installs generate a fresh operation id automatically.',
|
|
142
|
+
),
|
|
143
|
+
opts,
|
|
144
|
+
)
|
|
145
|
+
}
|
|
112
146
|
if (opts.direct) {
|
|
113
147
|
await installDirect(target, opts)
|
|
114
148
|
return
|
|
@@ -197,8 +231,9 @@ export async function installViaAdmin(
|
|
|
197
231
|
log.dim(` origin: ${result.origin}`)
|
|
198
232
|
log.dim(` url: ${result.url}`)
|
|
199
233
|
})
|
|
200
|
-
} catch (
|
|
201
|
-
|
|
234
|
+
} catch (error) {
|
|
235
|
+
await formatKernelError(error, isMachine(opts), undefined, opts.debug)
|
|
236
|
+
process.exit(1)
|
|
202
237
|
}
|
|
203
238
|
}
|
|
204
239
|
|
|
@@ -326,9 +361,27 @@ async function activeSlug(): Promise<string | undefined> {
|
|
|
326
361
|
* bypassing the admin catalog, with the identity-override
|
|
327
362
|
* consent gate. Works on any instance the caller can authenticate to.
|
|
328
363
|
*/
|
|
329
|
-
|
|
364
|
+
interface DirectInstallDependencies {
|
|
365
|
+
readonly acceptOperationId: (input: unknown) => string
|
|
366
|
+
readonly createOperationId: () => string
|
|
367
|
+
readonly runKernelCommand: typeof runKernelCommand
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const defaultDirectInstallDependencies: DirectInstallDependencies = Object.freeze({
|
|
371
|
+
acceptOperationId,
|
|
372
|
+
createOperationId,
|
|
373
|
+
runKernelCommand,
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
export async function installDirect(
|
|
377
|
+
target: string | undefined,
|
|
378
|
+
opts: InstallOpts,
|
|
379
|
+
dependencies: Partial<DirectInstallDependencies> = {},
|
|
380
|
+
): Promise<void> {
|
|
381
|
+
const direct = { ...defaultDirectInstallDependencies, ...dependencies }
|
|
330
382
|
let host = ''
|
|
331
383
|
let consentedOrigin: string | undefined
|
|
384
|
+
let operation: string
|
|
332
385
|
try {
|
|
333
386
|
if (!target) {
|
|
334
387
|
throw new AstraleError(
|
|
@@ -338,24 +391,30 @@ async function installDirect(target: string | undefined, opts: InstallOpts): Pro
|
|
|
338
391
|
)
|
|
339
392
|
}
|
|
340
393
|
host = validateInstallUrl(target)
|
|
394
|
+
operation =
|
|
395
|
+
opts.operation === undefined
|
|
396
|
+
? direct.createOperationId()
|
|
397
|
+
: direct.acceptOperationId(opts.operation)
|
|
341
398
|
consentedOrigin = await ensureIdentityOverrideConsent(
|
|
342
399
|
target,
|
|
343
400
|
host,
|
|
344
401
|
opts.allowIdentityOverride ?? false,
|
|
345
402
|
)
|
|
346
403
|
} catch (e) {
|
|
347
|
-
fatal(e)
|
|
404
|
+
fatal(e, opts)
|
|
348
405
|
}
|
|
349
406
|
const url = target as string
|
|
407
|
+
const retry = directInstallRetry(url, operation, opts)
|
|
350
408
|
|
|
351
|
-
await runKernelCommand<DirectInstallResult>({
|
|
409
|
+
await direct.runKernelCommand<DirectInstallResult>({
|
|
352
410
|
opts,
|
|
353
|
-
label: `Installing domain from ${url}`,
|
|
411
|
+
label: `Installing domain from ${url} (operation ${operation})`,
|
|
412
|
+
recovery: { operation, retry },
|
|
354
413
|
fn: async ({ session }) =>
|
|
355
414
|
(await session.call(
|
|
356
415
|
createPathCall(
|
|
357
416
|
Path.project(syscalls.install.ref).raw,
|
|
358
|
-
directInstallCallInput(url, opts.token),
|
|
417
|
+
directInstallCallInput(url, operation, opts.token),
|
|
359
418
|
),
|
|
360
419
|
)) as DirectInstallResult,
|
|
361
420
|
format: (result, fmtOpts, isRaw) => {
|
|
@@ -364,11 +423,10 @@ async function installDirect(target: string | undefined, opts: InstallOpts): Pro
|
|
|
364
423
|
return
|
|
365
424
|
}
|
|
366
425
|
const installed = result.transitions[0]?.intent
|
|
367
|
-
if (installed
|
|
368
|
-
throw new Error('Kernel install returned no committed Domain transition.')
|
|
369
|
-
}
|
|
426
|
+
if (!installed) throw new Error('Kernel install returned no committed Domain transition.')
|
|
370
427
|
const revision = installed.target?.schemaRevision ?? result.operation
|
|
371
428
|
log.success(`Domain installed: ${installed.origin}@${revision}`)
|
|
429
|
+
log.dim(` operation: ${result.operation}`)
|
|
372
430
|
// Belt-and-braces: the kernel-confirmed origin is authoritative. If it
|
|
373
431
|
// aliases the host and the pre-install gate never consented to THAT
|
|
374
432
|
// origin (lying or absent `/meta`), say so loudly after the fact.
|
|
@@ -383,6 +441,11 @@ async function installDirect(target: string | undefined, opts: InstallOpts): Pro
|
|
|
383
441
|
})
|
|
384
442
|
}
|
|
385
443
|
|
|
444
|
+
function directInstallRetry(url: string, operation: string, opts: InstallOpts): string {
|
|
445
|
+
const instance = opts.instance === undefined ? '' : ` -i ${opts.instance}`
|
|
446
|
+
return `astrale domain install ${url} --direct --operation ${operation}${instance}`
|
|
447
|
+
}
|
|
448
|
+
|
|
386
449
|
function validateInstallUrl(value: string): string {
|
|
387
450
|
let url: URL
|
|
388
451
|
try {
|
|
@@ -37,6 +37,46 @@ describe('formatKernelError', () => {
|
|
|
37
37
|
expect(writes[0]).not.toContain('ECONNREFUSED')
|
|
38
38
|
})
|
|
39
39
|
|
|
40
|
+
test('retains operation recovery only for outcome-unknown transport failure', async () => {
|
|
41
|
+
const writes: string[] = []
|
|
42
|
+
const original = process.stderr.write
|
|
43
|
+
process.stderr.write = ((chunk: string | Uint8Array) => {
|
|
44
|
+
writes.push(typeof chunk === 'string' ? chunk : new TextDecoder().decode(chunk))
|
|
45
|
+
return true
|
|
46
|
+
}) as typeof process.stderr.write
|
|
47
|
+
try {
|
|
48
|
+
await formatKernelError(
|
|
49
|
+
new TransportError('Request timed out.', {
|
|
50
|
+
cause: new Error('timeout'),
|
|
51
|
+
phase: 'timeout',
|
|
52
|
+
delivery: 'unknown',
|
|
53
|
+
}),
|
|
54
|
+
true,
|
|
55
|
+
undefined,
|
|
56
|
+
false,
|
|
57
|
+
{
|
|
58
|
+
recovery: {
|
|
59
|
+
operation: '4a4c9a18-50f6-4d84-a7b7-2d83e3e45dc8',
|
|
60
|
+
retry:
|
|
61
|
+
'astrale domain install https://crm.test --direct --operation 4a4c9a18-50f6-4d84-a7b7-2d83e3e45dc8',
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
)
|
|
65
|
+
} finally {
|
|
66
|
+
process.stderr.write = original
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
expect(writes).toHaveLength(1)
|
|
70
|
+
expect(JSON.parse(writes[0]!)).toMatchObject({
|
|
71
|
+
error: 'TIMEOUT',
|
|
72
|
+
phase: 'timeout',
|
|
73
|
+
delivery: 'unknown',
|
|
74
|
+
operation: '4a4c9a18-50f6-4d84-a7b7-2d83e3e45dc8',
|
|
75
|
+
retry:
|
|
76
|
+
'astrale domain install https://crm.test --direct --operation 4a4c9a18-50f6-4d84-a7b7-2d83e3e45dc8',
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
40
80
|
/** @evidence TEST-CLI-CONNECTION-PRESERVES-PUBLIC-SEMANTIC-REASON */
|
|
41
81
|
test('preserves a Kernel-admitted semantic reason in machine output', async () => {
|
|
42
82
|
const writes: string[] = []
|
|
@@ -275,8 +315,9 @@ describe('formatKernelError', () => {
|
|
|
275
315
|
details: {
|
|
276
316
|
phase: 'upgrade',
|
|
277
317
|
origin: 'grc.example',
|
|
278
|
-
|
|
279
|
-
|
|
318
|
+
issue: 'issuer-changed',
|
|
319
|
+
installedIssuer: 'https://old.example',
|
|
320
|
+
replacementIssuer: 'https://new.example',
|
|
280
321
|
},
|
|
281
322
|
}),
|
|
282
323
|
true,
|
|
@@ -294,18 +335,53 @@ describe('formatKernelError', () => {
|
|
|
294
335
|
details: {
|
|
295
336
|
phase: 'upgrade',
|
|
296
337
|
origin: 'grc.example',
|
|
297
|
-
|
|
298
|
-
|
|
338
|
+
issue: 'issuer-changed',
|
|
339
|
+
installedIssuer: 'https://old.example',
|
|
340
|
+
replacementIssuer: 'https://new.example',
|
|
299
341
|
},
|
|
300
342
|
},
|
|
301
343
|
hint: schemaUpgradeHint({
|
|
302
344
|
origin: 'grc.example',
|
|
303
|
-
|
|
304
|
-
|
|
345
|
+
issue: 'issuer-changed',
|
|
346
|
+
installedIssuer: 'https://old.example',
|
|
347
|
+
replacementIssuer: 'https://new.example',
|
|
305
348
|
}),
|
|
306
349
|
})
|
|
307
350
|
})
|
|
308
351
|
|
|
352
|
+
test('prints both issuers and the recovery command for an incompatible replacement', async () => {
|
|
353
|
+
const errors: string[] = []
|
|
354
|
+
const details: string[] = []
|
|
355
|
+
const originalError = console.error
|
|
356
|
+
const originalLog = console.log
|
|
357
|
+
console.error = (...values: unknown[]) => errors.push(values.map(String).join(' '))
|
|
358
|
+
console.log = (...values: unknown[]) => details.push(values.map(String).join(' '))
|
|
359
|
+
try {
|
|
360
|
+
await formatKernelError(
|
|
361
|
+
new ResponseError(5001, 'Schema operation is not supported.', {
|
|
362
|
+
code: 'SCHEMA_UPGRADE_INCOMPATIBLE',
|
|
363
|
+
details: {
|
|
364
|
+
phase: 'upgrade',
|
|
365
|
+
origin: 'grc.example',
|
|
366
|
+
issue: 'issuer-changed',
|
|
367
|
+
installedIssuer: 'https://old.example',
|
|
368
|
+
replacementIssuer: 'https://new.example',
|
|
369
|
+
},
|
|
370
|
+
}),
|
|
371
|
+
false,
|
|
372
|
+
)
|
|
373
|
+
} finally {
|
|
374
|
+
console.error = originalError
|
|
375
|
+
console.log = originalLog
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
expect(errors.join('\n')).toContain('RESPONSE_ERROR(5001)')
|
|
379
|
+
expect(details.join('\n')).toContain('reason: SCHEMA_UPGRADE_INCOMPATIBLE')
|
|
380
|
+
expect(details.join('\n')).toContain('installed issuer: https://old.example')
|
|
381
|
+
expect(details.join('\n')).toContain('replacement issuer: https://new.example')
|
|
382
|
+
expect(details.join('\n')).toContain('astrale domain uninstall grc.example')
|
|
383
|
+
})
|
|
384
|
+
|
|
309
385
|
test('explains a private Domain source without exposing transport diagnostics', async () => {
|
|
310
386
|
const writes: string[] = []
|
|
311
387
|
const original = process.stderr.write
|
|
@@ -16,6 +16,11 @@ export interface KernelCommandOpts extends ConnectionOptions {
|
|
|
16
16
|
readonly debug?: boolean
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
export interface OperationRecovery {
|
|
20
|
+
readonly operation: string
|
|
21
|
+
readonly retry: string
|
|
22
|
+
}
|
|
23
|
+
|
|
19
24
|
/**
|
|
20
25
|
* Encapsulates the standard kernel command lifecycle:
|
|
21
26
|
* spinner → connect → call → timing → output → error handling.
|
|
@@ -27,6 +32,7 @@ export interface KernelCommandOpts extends ConnectionOptions {
|
|
|
27
32
|
export async function runKernelCommand<T>(input: {
|
|
28
33
|
readonly opts: KernelCommandOpts
|
|
29
34
|
readonly label: string
|
|
35
|
+
readonly recovery?: OperationRecovery
|
|
30
36
|
readonly fn: (context: ConnectionContext) => Promise<T>
|
|
31
37
|
readonly format?: (
|
|
32
38
|
result: T,
|
|
@@ -53,7 +59,9 @@ export async function runKernelCommand<T>(input: {
|
|
|
53
59
|
}
|
|
54
60
|
} catch (error) {
|
|
55
61
|
if (!isRaw && spin) spin.fail(`${label} failed`)
|
|
56
|
-
await formatKernelError(error, isRaw, undefined, opts.debug
|
|
62
|
+
await formatKernelError(error, isRaw, undefined, opts.debug, {
|
|
63
|
+
recovery: input.recovery,
|
|
64
|
+
})
|
|
57
65
|
process.exit(1)
|
|
58
66
|
}
|
|
59
67
|
}
|
package/src/connection/errors.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk'
|
|
2
2
|
|
|
3
|
+
import type { OperationRecovery } from './command'
|
|
4
|
+
|
|
3
5
|
import { AstraleError } from '../errors'
|
|
4
6
|
import { readLocalStatus, type LocalStatus } from '../lib/local-status'
|
|
5
7
|
import { log } from '../lib/log'
|
|
@@ -28,6 +30,7 @@ export async function formatKernelError(
|
|
|
28
30
|
isRaw: boolean,
|
|
29
31
|
urlArg = '',
|
|
30
32
|
debug = false,
|
|
33
|
+
opts: { recovery?: OperationRecovery } = {},
|
|
31
34
|
): Promise<void> {
|
|
32
35
|
const url =
|
|
33
36
|
urlArg || (error instanceof Error ? ((error as Error & { url?: string }).url ?? '') : '')
|
|
@@ -54,7 +57,7 @@ export async function formatKernelError(
|
|
|
54
57
|
|
|
55
58
|
switch (name) {
|
|
56
59
|
case 'TransportError':
|
|
57
|
-
presentTransportError(error, isRaw, url, localContext)
|
|
60
|
+
presentTransportError(error, isRaw, url, localContext, opts.recovery)
|
|
58
61
|
break
|
|
59
62
|
|
|
60
63
|
case 'ResponseError': {
|
|
@@ -96,11 +99,9 @@ export async function formatKernelError(
|
|
|
96
99
|
}
|
|
97
100
|
presentFunctionInputIssues(inputIssues)
|
|
98
101
|
if (queryRepair !== undefined) presentQueryInputRepair(queryRepair)
|
|
99
|
-
if (upgrade?.
|
|
100
|
-
log.dim(` installed issuer: ${upgrade.
|
|
101
|
-
|
|
102
|
-
if (upgrade?.actual !== undefined) {
|
|
103
|
-
log.dim(` replacement issuer: ${upgrade.actual}`)
|
|
102
|
+
if (upgrade?.issue === 'issuer-changed') {
|
|
103
|
+
log.dim(` installed issuer: ${upgrade.installedIssuer}`)
|
|
104
|
+
log.dim(` replacement issuer: ${upgrade.replacementIssuer}`)
|
|
104
105
|
}
|
|
105
106
|
if (hint !== undefined) log.dim(` ${hint}`)
|
|
106
107
|
}
|
|
@@ -194,6 +195,7 @@ function presentTransportError(
|
|
|
194
195
|
isRaw: boolean,
|
|
195
196
|
url: string,
|
|
196
197
|
context: LocalStatus | undefined,
|
|
198
|
+
recovery: OperationRecovery | undefined,
|
|
197
199
|
): void {
|
|
198
200
|
const phase = transportPhase(error)
|
|
199
201
|
const delivery = transportDelivery(error)
|
|
@@ -213,6 +215,7 @@ function presentTransportError(
|
|
|
213
215
|
...(phase === undefined ? {} : { phase }),
|
|
214
216
|
...(delivery === undefined ? {} : { delivery }),
|
|
215
217
|
...(context === undefined ? {} : { context }),
|
|
218
|
+
...(delivery === 'unknown' && recovery !== undefined ? recovery : {}),
|
|
216
219
|
})
|
|
217
220
|
return
|
|
218
221
|
}
|
|
@@ -221,10 +224,18 @@ function presentTransportError(
|
|
|
221
224
|
if (phase !== undefined) log.dim(` phase: ${phase}`)
|
|
222
225
|
if (phase === 'connect') log.dim(' Check the target and run `astrale status`.')
|
|
223
226
|
else if (phase === 'timeout') log.dim(' Try increasing `--timeout`.')
|
|
224
|
-
else if (delivery === 'unknown')
|
|
227
|
+
else if (delivery === 'unknown') printOperationRecovery(recovery)
|
|
228
|
+
printLocalContext(context)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function printOperationRecovery(recovery: OperationRecovery | undefined): void {
|
|
232
|
+
if (recovery === undefined) {
|
|
225
233
|
log.dim(' Delivery is unknown; do not automatically retry a mutating call.')
|
|
234
|
+
return
|
|
226
235
|
}
|
|
227
|
-
|
|
236
|
+
log.dim(' Delivery is unknown; retry with the same operation id:')
|
|
237
|
+
log.dim(` operation: ${recovery.operation}`)
|
|
238
|
+
log.dim(` ${recovery.retry}`)
|
|
228
239
|
}
|
|
229
240
|
|
|
230
241
|
function presentFunctionInputIssues(issues: readonly FunctionInputIssue[]): void {
|
package/src/connection/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { createPathCall } from './call'
|
|
2
2
|
export { runKernelCommand } from './command'
|
|
3
|
-
export type { KernelCommandOpts } from './command'
|
|
3
|
+
export type { KernelCommandOpts, OperationRecovery } from './command'
|
|
4
4
|
export { expandSelfInCall, expandSelfInPath, withSelfHint } from './self'
|
|
5
5
|
export type { SelfExpansionMeta } from './self'
|
|
6
6
|
export { withAdminClientSession, withClientSession, type ConnectionContext } from './session'
|
|
@@ -15,12 +15,17 @@ export type QueryInputRepair =
|
|
|
15
15
|
path?: string
|
|
16
16
|
}>
|
|
17
17
|
|
|
18
|
-
export type SchemaUpgradeDetails =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
export type SchemaUpgradeDetails =
|
|
19
|
+
| {
|
|
20
|
+
readonly origin?: string
|
|
21
|
+
readonly issue?: undefined
|
|
22
|
+
}
|
|
23
|
+
| {
|
|
24
|
+
readonly origin: string
|
|
25
|
+
readonly issue: 'issuer-changed'
|
|
26
|
+
readonly installedIssuer: string
|
|
27
|
+
readonly replacementIssuer: string
|
|
28
|
+
}
|
|
24
29
|
|
|
25
30
|
const JSON_POINTER = /^(?:\/(?:[^~/]|~[01])*)*$/u
|
|
26
31
|
const MAXIMUM_FUNCTION_ISSUES = 32
|
|
@@ -115,18 +120,27 @@ export function queryInputRepair(reason: unknown): QueryInputRepair | undefined
|
|
|
115
120
|
export function schemaUpgradeDetails(reason: unknown): SchemaUpgradeDetails | undefined {
|
|
116
121
|
if (!record(reason) || reason.code !== 'SCHEMA_UPGRADE_INCOMPATIBLE') return undefined
|
|
117
122
|
const details = record(reason.details) ? reason.details : {}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
+
const origin = typeof details.origin === 'string' ? details.origin : undefined
|
|
124
|
+
if (
|
|
125
|
+
details.issue === 'issuer-changed' &&
|
|
126
|
+
origin !== undefined &&
|
|
127
|
+
typeof details.installedIssuer === 'string' &&
|
|
128
|
+
typeof details.replacementIssuer === 'string'
|
|
129
|
+
) {
|
|
130
|
+
return {
|
|
131
|
+
origin,
|
|
132
|
+
issue: details.issue,
|
|
133
|
+
installedIssuer: details.installedIssuer,
|
|
134
|
+
replacementIssuer: details.replacementIssuer,
|
|
135
|
+
}
|
|
123
136
|
}
|
|
137
|
+
return origin === undefined ? {} : { origin }
|
|
124
138
|
}
|
|
125
139
|
|
|
126
140
|
export function schemaUpgradeHint(details: SchemaUpgradeDetails): string {
|
|
127
141
|
const target = details.origin ?? '<origin>'
|
|
128
142
|
const explanation =
|
|
129
|
-
details.
|
|
143
|
+
details.issue === 'issuer-changed'
|
|
130
144
|
? 'A replacement cannot change an installed Domain issuer.'
|
|
131
145
|
: 'The replacement changes an immutable part of the installed Domain.'
|
|
132
146
|
return (
|
|
@@ -187,7 +187,7 @@ describe('program composition', () => {
|
|
|
187
187
|
'whoami',
|
|
188
188
|
])
|
|
189
189
|
expect(createHash('sha256').update(JSON.stringify(surface)).digest('hex')).toBe(
|
|
190
|
-
'
|
|
190
|
+
'b21a8c98f0bb75432b460d4562c2d86f39ffb8e88349962e4611ccbf65fa9f73',
|
|
191
191
|
)
|
|
192
192
|
})
|
|
193
193
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as L}from"./index-
|
|
1
|
+
import{m as L}from"./index-LGSWRrk8.js";function S(y,b){for(var u=0;u<b.length;u++){const a=b[u];if(typeof a!="string"&&!Array.isArray(a)){for(const i in a)if(i!=="default"&&!(i in y)){const n=Object.getOwnPropertyDescriptor(a,i);n&&Object.defineProperty(y,i,n.get?n:{enumerable:!0,get:()=>a[i]})}}}return Object.freeze(Object.defineProperty(y,Symbol.toStringTag,{value:"Module"}))}function w(y){throw new Error('Could not dynamically require "'+y+'". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.')}var O={exports:{}},j;function C(){return j||(j=1,(function(y,b){(function(u){y.exports=u()})(function(){return(function(){function u(a,i,n){function d(f,_){if(!i[f]){if(!a[f]){var h=typeof w=="function"&&w;if(!_&&h)return h(f,!0);if(g)return g(f,!0);var o=new Error("Cannot find module '"+f+"'");throw o.code="MODULE_NOT_FOUND",o}var e=i[f]={exports:{}};a[f][0].call(e.exports,function(r){var t=a[f][1][r];return d(t||r)},e,e.exports,u,a,i,n)}return i[f].exports}for(var g=typeof w=="function"&&w,m=0;m<n.length;m++)d(n[m]);return d}return u})()({1:[function(u,a,i){Object.defineProperty(i,"__esModule",{value:!0}),i.default=void 0;function n(o){"@babel/helpers - typeof";return n=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(e){return typeof e}:function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(o)}function d(o,e){if(!(o instanceof e))throw new TypeError("Cannot call a class as a function")}function g(o,e){for(var r=0;r<e.length;r++){var t=e[r];t.enumerable=t.enumerable||!1,t.configurable=!0,"value"in t&&(t.writable=!0),Object.defineProperty(o,f(t.key),t)}}function m(o,e,r){return e&&g(o.prototype,e),Object.defineProperty(o,"prototype",{writable:!1}),o}function f(o){var e=_(o,"string");return n(e)=="symbol"?e:e+""}function _(o,e){if(n(o)!="object"||!o)return o;var r=o[Symbol.toPrimitive];if(r!==void 0){var t=r.call(o,e);if(n(t)!="object")return t;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(o)}i.default=(function(){function o(){var e=this,r=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},t=r.defaultLayoutOptions,s=t===void 0?{}:t,l=r.algorithms,v=l===void 0?["layered","stress","mrtree","radial","force","disco","sporeOverlap","sporeCompaction","rectpacking"]:l,c=r.workerFactory,p=r.workerUrl;if(d(this,o),this.defaultLayoutOptions=s,this.initialized=!1,typeof p>"u"&&typeof c>"u")throw new Error("Cannot construct an ELK without both 'workerUrl' and 'workerFactory'.");var k=c;typeof p<"u"&&typeof c>"u"&&(k=function(M){return new Worker(M)});var E=k(p);if(typeof E.postMessage!="function")throw new TypeError("Created worker does not provide the required 'postMessage' function.");this.worker=new h(E),this.worker.postMessage({cmd:"register",algorithms:v}).then(function(P){return e.initialized=!0}).catch(console.err)}return m(o,[{key:"layout",value:function(r){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},s=t.layoutOptions,l=s===void 0?this.defaultLayoutOptions:s,v=t.logging,c=v===void 0?!1:v,p=t.measureExecutionTime,k=p===void 0?!1:p;return r?this.worker.postMessage({cmd:"layout",graph:r,layoutOptions:l,options:{logging:c,measureExecutionTime:k}}):Promise.reject(new Error("Missing mandatory parameter 'graph'."))}},{key:"knownLayoutAlgorithms",value:function(){return this.worker.postMessage({cmd:"algorithms"})}},{key:"knownLayoutOptions",value:function(){return this.worker.postMessage({cmd:"options"})}},{key:"knownLayoutCategories",value:function(){return this.worker.postMessage({cmd:"categories"})}},{key:"terminateWorker",value:function(){this.worker&&this.worker.terminate()}}])})();var h=(function(){function o(e){var r=this;if(d(this,o),e===void 0)throw new Error("Missing mandatory parameter 'worker'.");this.resolvers={},this.worker=e,this.worker.onmessage=function(t){setTimeout(function(){r.receive(r,t)},0)}}return m(o,[{key:"postMessage",value:function(r){var t=this.id||0;this.id=t+1,r.id=t;var s=this;return new Promise(function(l,v){s.resolvers[t]=function(c,p){c?(s.convertGwtStyleError(c),v(c)):l(p)},s.worker.postMessage(r)})}},{key:"receive",value:function(r,t){var s=t.data,l=r.resolvers[s.id];l&&(delete r.resolvers[s.id],s.error?l(s.error):l(null,s.data))}},{key:"terminate",value:function(){this.worker&&this.worker.terminate()}},{key:"convertGwtStyleError",value:function(r){if(r){var t=r.__java$exception;t&&(t.cause&&t.cause.backingJsObject&&(r.cause=t.cause.backingJsObject,this.convertGwtStyleError(r.cause)),delete r.__java$exception)}}}])})()},{}],2:[function(u,a,i){var n=u("./elk-api.js").default;Object.defineProperty(a.exports,"__esModule",{value:!0}),a.exports=n,n.default=n},{"./elk-api.js":1}]},{},[2])(2)})})(O)),O.exports}var x=C();const A=L(x),q=S({__proto__:null,default:A},[x]);export{q as e};
|