@cmodjs/core 4.0.2 → 4.1.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/docs/commands.md +43 -8
- package/docs/index.md +3 -3
- package/docs/install-steps.md +6 -6
- package/docs/jobs.md +2 -1
- package/docs/mod.md +1 -1
- package/docs/options.md +1 -1
- package/docs/permissions.md +1 -1
- package/docs/session.md +44 -13
- package/docs/testing.md +14 -10
- package/docs/ui.md +43 -11
- package/installer.d.ts +3 -2
- package/installer.d.ts.map +1 -1
- package/jobs/program.d.ts +1 -0
- package/jobs/program.d.ts.map +1 -1
- package/jobs/program.js +39 -23
- package/mod.d.ts +3 -2
- package/mod.d.ts.map +1 -1
- package/mod.js +1 -0
- package/package.json +1 -1
- package/records.d.ts +15 -1
- package/records.d.ts.map +1 -1
- package/records.js +39 -1
- package/register.d.ts.map +1 -1
- package/register.js +5 -2
- package/runtime/before.d.ts +6 -2
- package/runtime/before.d.ts.map +1 -1
- package/runtime/before.js +56 -18
- package/runtime/buttons.d.ts +10 -0
- package/runtime/buttons.d.ts.map +1 -0
- package/runtime/buttons.js +12 -0
- package/runtime/conversations.d.ts.map +1 -1
- package/runtime/conversations.js +134 -25
- package/runtime/engine.d.ts +2 -0
- package/runtime/engine.d.ts.map +1 -1
- package/runtime/grants.d.ts +1 -0
- package/runtime/grants.d.ts.map +1 -1
- package/runtime/grants.js +5 -2
- package/runtime/hotkeys.d.ts.map +1 -1
- package/runtime/hotkeys.js +7 -4
- package/runtime/installer.d.ts +9 -6
- package/runtime/installer.d.ts.map +1 -1
- package/runtime/installer.js +104 -47
- package/runtime/lifecycle.d.ts.map +1 -1
- package/runtime/lifecycle.js +121 -40
- package/runtime/pages.d.ts +3 -1
- package/runtime/pages.d.ts.map +1 -1
- package/runtime/pages.js +34 -10
- package/runtime/parts.d.ts +1 -0
- package/runtime/parts.d.ts.map +1 -1
- package/runtime/parts.js +6 -3
- package/runtime/programs.d.ts +1 -0
- package/runtime/programs.d.ts.map +1 -1
- package/runtime/programs.js +4 -1
- package/runtime/sessions.d.ts +2 -2
- package/runtime/sessions.d.ts.map +1 -1
- package/runtime/sessions.js +88 -58
- package/runtime/toaster.d.ts.map +1 -1
- package/runtime/toaster.js +23 -9
- package/runtime/ui.d.ts +21 -4
- package/runtime/ui.d.ts.map +1 -1
- package/runtime/ui.js +158 -58
- package/runtime/watches.d.ts.map +1 -1
- package/runtime/watches.js +18 -4
- package/runtime/widgets.d.ts +7 -5
- package/runtime/widgets.d.ts.map +1 -1
- package/runtime/widgets.js +29 -20
- package/session/context/turn/message/attached-file.d.ts +13 -0
- package/session/context/turn/message/attached-file.d.ts.map +1 -0
- package/session/context/turn/message/attached-file.js +21 -0
- package/session/context/turn/message/message.d.ts +3 -2
- package/session/context/turn/message/message.d.ts.map +1 -1
- package/session/context/turn/message/message.js +20 -6
- package/session/conversation.d.ts +14 -3
- package/session/conversation.d.ts.map +1 -1
- package/session/conversation.js +23 -4
- package/session/goal/task.d.ts +5 -0
- package/session/goal/task.d.ts.map +1 -1
- package/session/session.d.ts +35 -7
- package/session/session.d.ts.map +1 -1
- package/session/session.js +2 -1
- package/src/installer.ts +3 -2
- package/src/jobs/program.ts +41 -21
- package/src/mod.ts +3 -2
- package/src/records.ts +49 -1
- package/src/register.ts +9 -6
- package/src/runtime/before.ts +61 -22
- package/src/runtime/buttons.ts +22 -0
- package/src/runtime/conversations.ts +138 -32
- package/src/runtime/engine.ts +2 -0
- package/src/runtime/grants.ts +6 -2
- package/src/runtime/hotkeys.ts +7 -4
- package/src/runtime/installer.ts +144 -82
- package/src/runtime/lifecycle.ts +128 -43
- package/src/runtime/pages.ts +45 -8
- package/src/runtime/parts.ts +8 -4
- package/src/runtime/programs.ts +5 -1
- package/src/runtime/sessions.ts +83 -57
- package/src/runtime/toaster.ts +27 -9
- package/src/runtime/ui.ts +163 -61
- package/src/runtime/watches.ts +20 -5
- package/src/runtime/widgets.ts +34 -22
- package/src/session/context/turn/message/attached-file.ts +36 -0
- package/src/session/context/turn/message/message.ts +20 -7
- package/src/session/conversation.ts +30 -6
- package/src/session/goal/task.ts +6 -0
- package/src/session/session.ts +17 -7
- package/src/testing/fake-claude.ts +21 -6
- package/src/testing/fake-elements.ts +51 -8
- package/src/testing/fake-sessions.ts +60 -23
- package/src/testing.ts +86 -28
- package/src/ui/areas.ts +1 -0
- package/src/ui/components.ts +14 -8
- package/src/ui/elements.ts +6 -2
- package/src/ui/markdown.ts +3 -3
- package/src/ui/panes.ts +2 -2
- package/src/ui/styles.ts +6 -2
- package/testing/fake-claude.d.ts +3 -0
- package/testing/fake-claude.d.ts.map +1 -1
- package/testing/fake-claude.js +18 -5
- package/testing/fake-elements.d.ts.map +1 -1
- package/testing/fake-elements.js +46 -7
- package/testing/fake-sessions.d.ts +21 -10
- package/testing/fake-sessions.d.ts.map +1 -1
- package/testing/fake-sessions.js +58 -20
- package/testing.d.ts +8 -12
- package/testing.d.ts.map +1 -1
- package/testing.js +82 -21
- package/ui/areas.d.ts +4 -0
- package/ui/areas.d.ts.map +1 -1
- package/ui/components.d.ts +1 -0
- package/ui/components.d.ts.map +1 -1
- package/ui/components.js +11 -7
- package/ui/elements.d.ts +3 -0
- package/ui/elements.d.ts.map +1 -1
- package/ui/elements.js +6 -1
- package/ui/markdown.d.ts +2 -2
- package/ui/markdown.d.ts.map +1 -1
- package/ui/panes.d.ts +2 -1
- package/ui/panes.d.ts.map +1 -1
- package/ui/styles.d.ts +11 -2
- package/ui/styles.d.ts.map +1 -1
package/src/runtime/installer.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RenderElement } from 'claude-code'
|
|
2
2
|
import type { Step } from '../installer.js'
|
|
3
3
|
import type { Mod } from '../mod.js'
|
|
4
4
|
import type { Option } from '../options.js'
|
|
5
|
-
import { permissionWords, secretFix } from '../records.js'
|
|
6
|
-
import { Box,
|
|
5
|
+
import { isObject, permissionWords, secretFix } from '../records.js'
|
|
6
|
+
import { Box, elementData, Input, Text, type Handler } from '../ui/elements.js'
|
|
7
|
+
import { filledButton } from './buttons.js'
|
|
8
|
+
import type { PageProps } from '../ui/panes.js'
|
|
7
9
|
import { messageOf } from '../utils/text.js'
|
|
8
10
|
import type { Claude } from './claude.js'
|
|
11
|
+
import { cmodPluginName, withBareRefusals } from './dependencies.js'
|
|
12
|
+
import { merged, partMethods, type DrawnPart, type PartPress, type PartsUpdate, type ShownPages } from './ui.js'
|
|
9
13
|
|
|
10
|
-
export const
|
|
14
|
+
export const installPage = 'cmod:install'
|
|
11
15
|
|
|
12
16
|
export type Consent = {
|
|
13
17
|
readonly install: string
|
|
@@ -17,65 +21,122 @@ export type Consent = {
|
|
|
17
21
|
readonly updates?: string | undefined
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
type
|
|
21
|
-
draw(props:
|
|
24
|
+
type Question = {
|
|
25
|
+
draw(props: PageProps<undefined>): RenderElement
|
|
22
26
|
cancel(): void
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
export type Installer = {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
closed(id: string): void
|
|
30
|
+
answer(method: string, input: unknown): Promise<{ readonly value: unknown }> | undefined
|
|
31
|
+
hear(shown: ShownPages): void
|
|
29
32
|
consent(consent: Consent): Promise<boolean>
|
|
30
33
|
options(missing: readonly (readonly [string, Option])[], save: (key: string, value: string) => Promise<string | undefined>): Promise<boolean>
|
|
31
34
|
step<State extends object>(mod: Mod<State>, step: Step<State>, index: number, total: number): Promise<boolean>
|
|
32
35
|
close(): Promise<void>
|
|
36
|
+
shown(): Promise<void>
|
|
37
|
+
collect(run: () => Promise<unknown>): Promise<PartsUpdate>
|
|
33
38
|
}
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
const keptDraws = 8
|
|
41
|
+
|
|
42
|
+
export function createInstaller(name: string, claude: () => Claude, waiting: { show(open: () => Promise<void>): void; hide(): void }, nextDraw: () => number): Installer {
|
|
43
|
+
let question: Question | undefined
|
|
38
44
|
let isOpen = false
|
|
45
|
+
let isSeen = false
|
|
46
|
+
let waitingForShow: (() => void)[] = []
|
|
47
|
+
let collected: PartsUpdate[] | undefined
|
|
48
|
+
const draws = new Map<number, readonly Handler[]>()
|
|
39
49
|
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
await claude().
|
|
50
|
+
const send = async (update: PartsUpdate) => {
|
|
51
|
+
if (collected !== undefined) return void collected.push(update)
|
|
52
|
+
await withBareRefusals(claude()).cmod.call({ to: cmodPluginName, method: partMethods.changed, input: update })
|
|
43
53
|
}
|
|
44
54
|
|
|
45
|
-
const
|
|
46
|
-
|
|
55
|
+
const log = (what: string) => (error: unknown) => claude().ui.log(`${name}: ${what}: ${messageOf(error)}`, { to: 'debug' })
|
|
56
|
+
|
|
57
|
+
const open = (isAsked: boolean) => send({ requests: [{ open: installPage, focus: true }], ...(isAsked ? { isAsked: true } : {}) })
|
|
58
|
+
|
|
59
|
+
const show = async <Answer>(build: (answer: (value: Answer) => void) => Omit<Question, 'cancel'>, cancelled: Answer): Promise<Answer> => {
|
|
60
|
+
question?.cancel()
|
|
47
61
|
const answer = new Promise<Answer>((resolve) => {
|
|
62
|
+
let isAnswered = false
|
|
48
63
|
const done = (value: Answer) => {
|
|
49
|
-
if (
|
|
64
|
+
if (isAnswered) return
|
|
65
|
+
isAnswered = true
|
|
50
66
|
waiting.hide()
|
|
51
|
-
resolve(value)
|
|
67
|
+
claude().clock.after(0, () => resolve(value))
|
|
52
68
|
}
|
|
53
|
-
|
|
54
|
-
page = shown
|
|
55
|
-
})
|
|
56
|
-
waiting.show(open)
|
|
57
|
-
if (!isOpen) await open().catch((error: unknown) => {
|
|
58
|
-
waiting.hide()
|
|
59
|
-
throw error
|
|
69
|
+
question = { ...build(done), cancel: () => done(cancelled) }
|
|
60
70
|
})
|
|
61
|
-
|
|
71
|
+
waiting.show(() => open(true))
|
|
72
|
+
if (isOpen) {
|
|
73
|
+
await send({ redraw: true }).catch(log('the installer did not redraw'))
|
|
74
|
+
} else {
|
|
75
|
+
isOpen = true
|
|
76
|
+
await open(false).catch((error: unknown) => {
|
|
77
|
+
isOpen = false
|
|
78
|
+
waiting.hide()
|
|
79
|
+
throw error
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
for (const resolve of waitingForShow) resolve()
|
|
83
|
+
waitingForShow = []
|
|
62
84
|
return answer
|
|
63
85
|
}
|
|
64
86
|
|
|
65
|
-
const
|
|
66
|
-
|
|
87
|
+
const redraw = () => void send({ redraw: true }).catch(log('the installer did not redraw'))
|
|
88
|
+
|
|
89
|
+
const actions = (...buttons: readonly RenderElement[]) => Box({ gap: 2, flexShrink: 0, children: buttons })
|
|
90
|
+
|
|
91
|
+
const dialog = (title: RenderElement, ...children: readonly RenderElement[]) => Box({ flexDirection: 'column', gap: 1, children: [title, ...children] })
|
|
92
|
+
|
|
93
|
+
const bullet = (text: string) => Box({ gap: 1, children: [Text({ dimColor: true, children: '•' }), Text({ wrap: 'wrap', children: text })] })
|
|
67
94
|
|
|
68
|
-
const
|
|
95
|
+
const group = (heading: string, items: readonly string[]) => Box({ flexDirection: 'column', children: [Text({ bold: true, children: heading }), ...items.map(bullet)] })
|
|
96
|
+
|
|
97
|
+
const draw = (columns: number, isFocused: boolean): DrawnPart => {
|
|
98
|
+
if (question === undefined && !isOpen) claude().clock.after(0, () => void send({ requests: [{ close: installPage }] }).catch(log('a leftover install page stayed open')))
|
|
99
|
+
const handlers: Handler[] = []
|
|
100
|
+
const tree = elementData(question?.draw({ params: undefined, isFocused, columns }) ?? Text({ dimColor: true, children: `${name} needs nothing from you now.` }), handlers)
|
|
101
|
+
const drawn = nextDraw()
|
|
102
|
+
draws.set(drawn, handlers)
|
|
103
|
+
for (const kept of draws.keys()) if (draws.size > keptDraws) draws.delete(kept)
|
|
104
|
+
return { draw: drawn, tree }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const collect = async (run: () => unknown): Promise<PartsUpdate> => {
|
|
108
|
+
const outer = collected
|
|
109
|
+
const mine: PartsUpdate[] = []
|
|
110
|
+
collected = mine
|
|
111
|
+
try {
|
|
112
|
+
await run()
|
|
113
|
+
} finally {
|
|
114
|
+
collected = outer
|
|
115
|
+
}
|
|
116
|
+
return merged(mine)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const press = async ({ draw: drawn, handler, args }: PartPress): Promise<PartsUpdate> => {
|
|
120
|
+
const run = draws.get(drawn)?.[handler]
|
|
121
|
+
if (run === undefined) throw new Error(`${name}: that button drew before the last redraw. Press the one it shows now.`)
|
|
122
|
+
return collect(() => run(...args))
|
|
123
|
+
}
|
|
69
124
|
|
|
70
125
|
return {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
126
|
+
answer(method, input) {
|
|
127
|
+
if (method === partMethods.draw && isObject(input) && input['page'] === installPage) return Promise.resolve({ value: draw(Number(input['columns']), input['isFocused'] === true) })
|
|
128
|
+
if (method === partMethods.press && isObject(input) && typeof input['draw'] === 'number' && draws.has(input['draw'])) return press(input as PartPress).then((value) => ({ value }))
|
|
129
|
+
return undefined
|
|
74
130
|
},
|
|
75
|
-
|
|
76
|
-
if (id
|
|
131
|
+
hear(shown) {
|
|
132
|
+
if (shown.open.some((each) => each.id === installPage)) {
|
|
133
|
+
isSeen = isOpen
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
if (!isSeen) return
|
|
77
137
|
isOpen = false
|
|
78
|
-
|
|
138
|
+
isSeen = false
|
|
139
|
+
question?.cancel()
|
|
79
140
|
},
|
|
80
141
|
consent({ install, uninstall, hotkeys, permissions, updates }) {
|
|
81
142
|
const changes = [
|
|
@@ -87,14 +148,12 @@ export function createInstaller(name: string, claude: () => Claude, waiting: { s
|
|
|
87
148
|
return show<boolean>(
|
|
88
149
|
(answer) => ({
|
|
89
150
|
draw: () =>
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
[
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
],
|
|
97
|
-
[Button({ key: 'not-now', hotkey: 'n', label: 'Not now', onPress: () => answer(false) }), Button({ key: 'accept', hotkey: 'a', label: 'Accept', onPress: () => answer(true) })],
|
|
151
|
+
dialog(
|
|
152
|
+
Text({ bold: true, children: `Install ${name}?` }),
|
|
153
|
+
...(permissions.length === 0 ? [] : [group(`${name} wants to:`, permissions.map(permissionWords))]),
|
|
154
|
+
...(changes.length === 0 ? [] : [group(permissions.length === 0 ? `${name} wants to change your computer:` : 'and change your computer:', changes)]),
|
|
155
|
+
Text({ dimColor: true, wrap: 'wrap', children: 'You can turn each permission off later in Settings.' }),
|
|
156
|
+
actions(filledButton({ key: 'accept', hotkey: 'a', variant: 'primary', label: 'Accept', onPress: () => answer(true) }), filledButton({ key: 'not-now', hotkey: 'n', label: 'Not now', onPress: () => answer(false) })),
|
|
98
157
|
),
|
|
99
158
|
}),
|
|
100
159
|
false,
|
|
@@ -106,38 +165,36 @@ export function createInstaller(name: string, claude: () => Claude, waiting: { s
|
|
|
106
165
|
return show<boolean>(
|
|
107
166
|
(answer) => ({
|
|
108
167
|
draw: () =>
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
missing.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
flexDirection: 'column',
|
|
116
|
-
|
|
168
|
+
dialog(
|
|
169
|
+
Text({ bold: true, children: `Set up ${name}` }),
|
|
170
|
+
Text({ children: `${name} needs ${missing.length === 1 ? 'one setting' : `${missing.length} settings`}:` }),
|
|
171
|
+
...missing.map(([key, option]) => {
|
|
172
|
+
const label = Box({ flexDirection: 'column', children: [Text({ bold: true, children: option.title }), ...(option.description === '' ? [] : [Text({ dimColor: true, wrap: 'wrap', children: option.description })])] })
|
|
173
|
+
if (option.type === 'secret') {
|
|
174
|
+
return Box({ flexDirection: 'column', children: [label, Text({ dimColor: true, wrap: 'wrap', children: `${secretFix} Then press Next.` }), filledButton({ key: `secret:${key}`, label: 'Open /config', onPress: () => void claude().command.run({ command: 'config' }) })] })
|
|
175
|
+
}
|
|
176
|
+
const failure = failures.get(key)
|
|
177
|
+
const field = Input({
|
|
178
|
+
key: `option:${key}`,
|
|
179
|
+
label: 'Value',
|
|
180
|
+
placeholder: option.type === 'choice' ? (option.choices ?? []).join(', ') : option.type === 'toggle' ? 'true or false' : option.type === 'list' ? 'comma-separated' : '',
|
|
181
|
+
value: saved.get(key) ?? '',
|
|
182
|
+
submitLabel: 'save',
|
|
183
|
+
onSubmit: (value: string) =>
|
|
184
|
+
void save(key, value)
|
|
185
|
+
.then((refused) => {
|
|
186
|
+
if (refused === undefined) {
|
|
187
|
+
saved.set(key, value)
|
|
188
|
+
failures.delete(key)
|
|
189
|
+
} else failures.set(key, refused)
|
|
190
|
+
})
|
|
191
|
+
.catch((error: unknown) => failures.set(key, messageOf(error)))
|
|
192
|
+
.finally(redraw),
|
|
117
193
|
})
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const field = Input({
|
|
121
|
-
key: `option:${key}`,
|
|
122
|
-
label: 'Value',
|
|
123
|
-
placeholder: option.type === 'choice' ? (option.choices ?? []).join(', ') : option.type === 'toggle' ? 'true or false' : option.type === 'list' ? 'comma-separated' : '',
|
|
124
|
-
value: saved.get(key) ?? '',
|
|
125
|
-
submitLabel: 'save',
|
|
126
|
-
onSubmit: (value: string) =>
|
|
127
|
-
void save(key, value)
|
|
128
|
-
.then((refused) => {
|
|
129
|
-
if (refused === undefined) {
|
|
130
|
-
saved.set(key, value)
|
|
131
|
-
failures.delete(key)
|
|
132
|
-
} else failures.set(key, refused)
|
|
133
|
-
})
|
|
134
|
-
.catch((error: unknown) => failures.set(key, messageOf(error)))
|
|
135
|
-
.finally(() => claude().ui.invalidate('ui.render')),
|
|
136
|
-
})
|
|
137
|
-
const status = failure !== undefined ? Text({ color: 'error', children: failure }) : saved.has(key) ? Text({ color: 'success', children: '✔ saved' }) : Text({ children: '' })
|
|
138
|
-
return Box({ flexDirection: 'column', children: [label, field, status] })
|
|
194
|
+
const status = failure !== undefined ? [Text({ color: 'error', wrap: 'wrap', children: failure })] : saved.has(key) ? [Text({ color: 'success', children: '✓ saved' })] : []
|
|
195
|
+
return Box({ flexDirection: 'column', children: [label, field, ...status] })
|
|
139
196
|
}),
|
|
140
|
-
|
|
197
|
+
actions(filledButton({ key: 'next', hotkey: 'x', variant: 'primary', label: 'Next', onPress: () => answer(true) }), filledButton({ key: 'not-now', hotkey: 'n', label: 'Not now', onPress: () => answer(false) })),
|
|
141
198
|
),
|
|
142
199
|
}),
|
|
143
200
|
false,
|
|
@@ -152,7 +209,7 @@ export function createInstaller(name: string, claude: () => Claude, waiting: { s
|
|
|
152
209
|
try {
|
|
153
210
|
body = step.render(mod, props)
|
|
154
211
|
} catch (error) {
|
|
155
|
-
body = Text({ color: 'error', children: `The ${step.title} step could not draw: ${messageOf(error)}` })
|
|
212
|
+
body = Text({ color: 'error', wrap: 'wrap', children: `The ${step.title} step could not draw: ${messageOf(error)}` })
|
|
156
213
|
}
|
|
157
214
|
const next = async () => {
|
|
158
215
|
try {
|
|
@@ -161,12 +218,13 @@ export function createInstaller(name: string, claude: () => Claude, waiting: { s
|
|
|
161
218
|
} catch (error) {
|
|
162
219
|
notice = { color: 'error', text: `The ${step.title} step's isDone failed: ${messageOf(error)}` }
|
|
163
220
|
}
|
|
164
|
-
|
|
221
|
+
await send({ redraw: true })
|
|
165
222
|
}
|
|
166
|
-
return
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
223
|
+
return dialog(
|
|
224
|
+
Text({ children: [Text({ bold: true, children: step.title }), Text({ dimColor: true, children: ` (${index} of ${total})` })] }),
|
|
225
|
+
body,
|
|
226
|
+
...(notice === undefined ? [] : [Text({ color: notice.color, wrap: 'wrap', children: notice.text })]),
|
|
227
|
+
actions(filledButton({ key: 'next', hotkey: 'x', variant: 'primary', label: index === total ? 'Finish' : 'Next', onPress: () => void next() }), filledButton({ key: 'not-now', hotkey: 'n', label: 'Not now', onPress: () => answer(false) })),
|
|
170
228
|
)
|
|
171
229
|
},
|
|
172
230
|
}),
|
|
@@ -174,10 +232,14 @@ export function createInstaller(name: string, claude: () => Claude, waiting: { s
|
|
|
174
232
|
)
|
|
175
233
|
},
|
|
176
234
|
async close() {
|
|
177
|
-
|
|
235
|
+
question?.cancel()
|
|
236
|
+
question = undefined
|
|
178
237
|
if (!isOpen) return
|
|
179
238
|
isOpen = false
|
|
180
|
-
|
|
239
|
+
isSeen = false
|
|
240
|
+
await send({ requests: [{ close: installPage }] }).catch(log('the install page stayed open'))
|
|
181
241
|
},
|
|
242
|
+
shown: () => new Promise<void>((resolve) => void waitingForShow.push(resolve)),
|
|
243
|
+
collect,
|
|
182
244
|
}
|
|
183
245
|
}
|