orizu 0.5.49 → 0.5.50
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/dist/account-directory.d.ts +2 -2
- package/dist/account-directory.d.ts.map +1 -1
- package/dist/account-directory.js +7 -32
- package/dist/auth-commands.d.ts.map +1 -1
- package/dist/auth-commands.js +22 -30
- package/dist/credentials.d.ts +5 -0
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +84 -3
- package/dist/dataset-transfer-upload.d.ts.map +1 -1
- package/dist/dataset-transfer-upload.js +6 -1
- package/dist/directory-list-read.d.ts +37 -0
- package/dist/directory-list-read.d.ts.map +1 -0
- package/dist/directory-list-read.js +87 -0
- package/dist/hosted-boot.d.ts.map +1 -1
- package/dist/hosted-boot.js +59 -41
- package/dist/hosted-harness-opencode.d.ts +31 -0
- package/dist/hosted-harness-opencode.d.ts.map +1 -1
- package/dist/hosted-harness-opencode.js +45 -7
- package/dist/hosted-loop.d.ts +39 -2
- package/dist/hosted-loop.d.ts.map +1 -1
- package/dist/hosted-loop.js +127 -18
- package/dist/hosted-session-seed.d.ts +2 -0
- package/dist/hosted-session-seed.d.ts.map +1 -1
- package/dist/hosted-session-seed.js +27 -10
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +39 -234
- package/dist/keyboard-select.d.ts.map +1 -1
- package/dist/keyboard-select.js +23 -45
- package/dist/project-create-workflow.d.ts +18 -0
- package/dist/project-create-workflow.d.ts.map +1 -0
- package/dist/project-create-workflow.js +121 -0
- package/dist/self-update-command.d.ts +8 -0
- package/dist/self-update-command.d.ts.map +1 -0
- package/dist/self-update-command.js +390 -0
- package/dist/self-update.d.ts +29 -16
- package/dist/self-update.d.ts.map +1 -1
- package/dist/self-update.js +55 -244
- package/dist/setup-account.d.ts.map +1 -1
- package/dist/setup-account.js +4 -1
- package/dist/setup-and-skills-commands.d.ts.map +1 -1
- package/dist/setup-and-skills-commands.js +88 -102
- package/dist/skill-install-workflow.d.ts +23 -0
- package/dist/skill-install-workflow.d.ts.map +1 -0
- package/dist/skill-install-workflow.js +184 -0
- package/dist/skill-installer.d.ts +8 -3
- package/dist/skill-installer.d.ts.map +1 -1
- package/dist/skill-installer.js +26 -33
- package/dist/stored-session-requests.d.ts +27 -0
- package/dist/stored-session-requests.d.ts.map +1 -0
- package/dist/stored-session-requests.js +264 -0
- package/dist/terminal-key-session.d.ts +23 -0
- package/dist/terminal-key-session.d.ts.map +1 -0
- package/dist/terminal-key-session.js +74 -0
- package/package.json +1 -1
- package/src/account-directory.ts +8 -42
- package/src/auth-commands.ts +20 -29
- package/src/credentials.ts +83 -3
- package/src/dataset-transfer-upload.ts +7 -1
- package/src/directory-list-read.ts +123 -0
- package/src/hosted-boot.ts +65 -43
- package/src/hosted-harness-opencode.ts +65 -9
- package/src/hosted-loop.ts +152 -18
- package/src/hosted-session-seed.ts +25 -8
- package/src/http.ts +39 -317
- package/src/keyboard-select.ts +18 -55
- package/src/project-create-workflow.ts +139 -0
- package/src/self-update-command.ts +419 -0
- package/src/self-update.ts +70 -266
- package/src/setup-account.ts +4 -1
- package/src/setup-and-skills-commands.ts +89 -121
- package/src/skill-install-workflow.ts +215 -0
- package/src/skill-installer.ts +31 -38
- package/src/stored-session-requests.ts +316 -0
- package/src/terminal-key-session.ts +88 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
// `orizu update` of an npm-global install (ORI-2424). self-update.ts loads
|
|
2
|
+
// this only once it has found such an install, so no other command, and not
|
|
3
|
+
// the daily background cache refresh, loads Effect. Effect owns the children
|
|
4
|
+
// this starts: each runs in the command's fiber, and a stop interrupts that
|
|
5
|
+
// fiber, which passes the signal on to the running child and waits for it
|
|
6
|
+
// within the stop cap.
|
|
7
|
+
import { spawn } from 'child_process';
|
|
8
|
+
import { stderr as errorOutput, stdout as output } from 'process';
|
|
9
|
+
import { Cause, Effect, Exit, Fiber, Schema } from 'effect';
|
|
10
|
+
import { sanitizeTerminalText } from './json-response.js';
|
|
11
|
+
import { cliInvocation, compareSemver, MANUAL_INSTALL_COMMAND, npmInvocation, resolveLatest, resolveUpdateRegistry, tryWriteCache, } from './self-update.js';
|
|
12
|
+
const CHILD_TIMEOUT_MS = 60_000;
|
|
13
|
+
const STDERR_TAIL_CHARS = 4_000;
|
|
14
|
+
const STOP_SIGNALS = ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGQUIT'];
|
|
15
|
+
// The CLI's stop cap (ORI-2402) is 15 s. A stopped child gets 10 s to exit,
|
|
16
|
+
// then is killed; reading which version is installed gets the last 5 s.
|
|
17
|
+
const STOP_CHILD_WAIT_MS = 10_000;
|
|
18
|
+
const STOP_VERSION_CHECK_MS = 5_000;
|
|
19
|
+
const PIPE_GRACE_MS = 200;
|
|
20
|
+
// What the skills refresh child reports (`orizu skills update --json`).
|
|
21
|
+
const SkillUpdate = Schema.Struct({
|
|
22
|
+
path: Schema.String,
|
|
23
|
+
action: Schema.String,
|
|
24
|
+
backup: Schema.optionalKey(Schema.String),
|
|
25
|
+
warning: Schema.optionalKey(Schema.String),
|
|
26
|
+
error: Schema.optionalKey(Schema.String),
|
|
27
|
+
});
|
|
28
|
+
const SkillsReport = Schema.Struct({ updates: Schema.Array(SkillUpdate) });
|
|
29
|
+
function testMs(name, fallback) {
|
|
30
|
+
const override = process.env[name];
|
|
31
|
+
if (override === undefined)
|
|
32
|
+
return fallback;
|
|
33
|
+
const parsed = Number(override);
|
|
34
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
35
|
+
}
|
|
36
|
+
const verifyTimeoutMs = () => testMs('ORIZU_UPDATE_TEST_VERIFY_TIMEOUT_MS', CHILD_TIMEOUT_MS);
|
|
37
|
+
// Stops reading a child's output, so a process it started that still holds
|
|
38
|
+
// the pipes can't keep this command waiting.
|
|
39
|
+
function releasePipes(child) {
|
|
40
|
+
child.stdout?.destroy();
|
|
41
|
+
child.stderr?.destroy();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Passes the stop's signal on to a running child, waits for it to exit until
|
|
45
|
+
* the stop's deadline, then kills it. Runs when the command's fiber is
|
|
46
|
+
* interrupted while it waits for the child. It waits for the child itself
|
|
47
|
+
* ('exit'), not its output pipes ('close'): a process the child started can
|
|
48
|
+
* hold those open long after the child is gone.
|
|
49
|
+
*/
|
|
50
|
+
function stopChild(child, stop) {
|
|
51
|
+
return Effect.callback(resume => {
|
|
52
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
53
|
+
releasePipes(child);
|
|
54
|
+
resume(Effect.void);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const kill = setTimeout(() => child.kill('SIGKILL'), Math.max(0, stop.deadline - Date.now()));
|
|
58
|
+
child.once('exit', () => {
|
|
59
|
+
clearTimeout(kill);
|
|
60
|
+
releasePipes(child);
|
|
61
|
+
resume(Effect.void);
|
|
62
|
+
});
|
|
63
|
+
child.kill(stop.signal ?? 'SIGTERM');
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function runStreaming(command, args, io, stop, options = {}) {
|
|
67
|
+
return Effect.callback(resume => {
|
|
68
|
+
let stderr = '';
|
|
69
|
+
let stdout = '';
|
|
70
|
+
let settled = false;
|
|
71
|
+
let timedOut = false;
|
|
72
|
+
let timer;
|
|
73
|
+
const finish = (status) => {
|
|
74
|
+
if (settled)
|
|
75
|
+
return;
|
|
76
|
+
settled = true;
|
|
77
|
+
if (timer)
|
|
78
|
+
clearTimeout(timer);
|
|
79
|
+
resume(Effect.succeed({ status, stderr: stderr.slice(-STDERR_TAIL_CHARS), stdout, timedOut }));
|
|
80
|
+
};
|
|
81
|
+
const child = spawn(command, args, {
|
|
82
|
+
env: { ...process.env, ORIZU_NO_UPDATE_CHECK: '1' },
|
|
83
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
84
|
+
});
|
|
85
|
+
if (options.timeoutMs !== undefined) {
|
|
86
|
+
timer = setTimeout(() => {
|
|
87
|
+
timedOut = true;
|
|
88
|
+
child.kill('SIGKILL');
|
|
89
|
+
}, options.timeoutMs);
|
|
90
|
+
}
|
|
91
|
+
child.stdout?.on('data', chunk => {
|
|
92
|
+
const text = sanitizeTerminalText(chunk);
|
|
93
|
+
if (options.captureStdout) {
|
|
94
|
+
stdout = `${stdout}${text}`;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (io.json)
|
|
98
|
+
errorOutput.write(text);
|
|
99
|
+
else
|
|
100
|
+
output.write(text);
|
|
101
|
+
});
|
|
102
|
+
child.stderr?.on('data', chunk => {
|
|
103
|
+
const text = sanitizeTerminalText(chunk);
|
|
104
|
+
stderr = `${stderr}${text}`.slice(-STDERR_TAIL_CHARS);
|
|
105
|
+
errorOutput.write(text);
|
|
106
|
+
});
|
|
107
|
+
child.once('error', error => {
|
|
108
|
+
stderr = `${stderr}${sanitizeTerminalText(error.message)}`;
|
|
109
|
+
finish(1);
|
|
110
|
+
});
|
|
111
|
+
child.once('close', code => finish(code ?? 1));
|
|
112
|
+
return Effect.suspend(() => {
|
|
113
|
+
if (timer)
|
|
114
|
+
clearTimeout(timer);
|
|
115
|
+
return stopChild(child, stop);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
// Runs a child to completion and keeps all it said; used for version checks.
|
|
120
|
+
function runCaptured(command, args, timeoutMs, stop) {
|
|
121
|
+
return Effect.callback(resume => {
|
|
122
|
+
let stdout = '';
|
|
123
|
+
let stderr = '';
|
|
124
|
+
let error = null;
|
|
125
|
+
let timedOut = false;
|
|
126
|
+
const child = spawn(command, args, {
|
|
127
|
+
env: { ...process.env, ORIZU_NO_UPDATE_CHECK: '1' },
|
|
128
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
129
|
+
});
|
|
130
|
+
const timer = setTimeout(() => {
|
|
131
|
+
timedOut = true;
|
|
132
|
+
child.kill('SIGKILL');
|
|
133
|
+
}, timeoutMs);
|
|
134
|
+
child.stdout?.setEncoding('utf8');
|
|
135
|
+
child.stderr?.setEncoding('utf8');
|
|
136
|
+
child.stdout?.on('data', (chunk) => { stdout += chunk; });
|
|
137
|
+
child.stderr?.on('data', (chunk) => { stderr += chunk; });
|
|
138
|
+
let settled = false;
|
|
139
|
+
let grace;
|
|
140
|
+
const settle = (status, signal) => {
|
|
141
|
+
if (settled)
|
|
142
|
+
return;
|
|
143
|
+
settled = true;
|
|
144
|
+
clearTimeout(timer);
|
|
145
|
+
if (grace)
|
|
146
|
+
clearTimeout(grace);
|
|
147
|
+
releasePipes(child);
|
|
148
|
+
resume(Effect.succeed({ status, signal, stdout, stderr, error, timedOut }));
|
|
149
|
+
};
|
|
150
|
+
child.once('error', failure => {
|
|
151
|
+
error = failure;
|
|
152
|
+
if (child.pid === undefined)
|
|
153
|
+
settle(null, null);
|
|
154
|
+
});
|
|
155
|
+
child.once('close', (status, signal) => settle(status, signal));
|
|
156
|
+
// Once the process is gone, its output gets a moment to arrive; a process
|
|
157
|
+
// it started that holds the pipes doesn't extend the timeout.
|
|
158
|
+
child.once('exit', (status, signal) => { grace = setTimeout(() => settle(status, signal), PIPE_GRACE_MS); });
|
|
159
|
+
return Effect.suspend(() => {
|
|
160
|
+
clearTimeout(timer);
|
|
161
|
+
if (grace)
|
|
162
|
+
clearTimeout(grace);
|
|
163
|
+
return stop ? stopChild(child, stop) : Effect.sync(() => { child.kill('SIGKILL'); releasePipes(child); });
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Refresh managed skills through the given CLI binary and classify the outcome
|
|
169
|
+
* from the child's --json report, never from its human text: an install with
|
|
170
|
+
* no skill targets reports 'none', the one state a caller of `update --json`
|
|
171
|
+
* most needs to see (ADR-001 coupling). The report's backups, warnings and
|
|
172
|
+
* failures are passed on, in human and JSON output alike.
|
|
173
|
+
*/
|
|
174
|
+
function refreshManagedSkills(command, args, io, stop) {
|
|
175
|
+
return Effect.gen(function* () {
|
|
176
|
+
const child = yield* runStreaming(command, [...args, 'skills', 'update', '--json'], io, stop, { timeoutMs: CHILD_TIMEOUT_MS, captureStdout: true });
|
|
177
|
+
const report = Schema.decodeUnknownOption(Schema.fromJsonString(SkillsReport))(child.stdout || '');
|
|
178
|
+
const updates = report._tag === 'Some' ? report.value.updates : null;
|
|
179
|
+
if (updates !== null && !io.json)
|
|
180
|
+
printSkillUpdates(io, updates, child.status === 0);
|
|
181
|
+
if (child.status !== 0)
|
|
182
|
+
return { status: child.status, timedOut: child.timedOut, skills: 'none', updates };
|
|
183
|
+
if (updates === null)
|
|
184
|
+
return { status: 1, timedOut: false, skills: 'none', updates };
|
|
185
|
+
if (updates.length === 0)
|
|
186
|
+
return { status: 0, timedOut: false, skills: 'none', updates };
|
|
187
|
+
const refreshed = updates.some(update => update.action !== 'already-current');
|
|
188
|
+
return { status: 0, timedOut: false, skills: refreshed ? 'refreshed' : 'already-current', updates };
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
// The same lines `orizu skills update` prints for each install.
|
|
192
|
+
function printSkillUpdates(io, updates, succeeded) {
|
|
193
|
+
if (updates.length === 0 && succeeded)
|
|
194
|
+
io.printLine('No Orizu skill installs found. Run `orizu install-skill` first.');
|
|
195
|
+
for (const update of updates) {
|
|
196
|
+
const path = sanitizeTerminalText(update.path);
|
|
197
|
+
const backup = update.backup ? `; moved your earlier folder to ${sanitizeTerminalText(update.backup)}` : '';
|
|
198
|
+
if (update.action === 'already-current')
|
|
199
|
+
io.printLine(`Current ${path}`);
|
|
200
|
+
else if (update.action === 'relinked')
|
|
201
|
+
io.printLine(`Relinked ${path}`);
|
|
202
|
+
else if (update.action === 'rewrote')
|
|
203
|
+
io.printLine(`Rewrote ${path} from the installed CLI skill${backup}`);
|
|
204
|
+
else if (update.action === 'failed')
|
|
205
|
+
io.printLine(`Failed ${path}: ${sanitizeTerminalText(update.error ?? 'unknown error')}`);
|
|
206
|
+
else
|
|
207
|
+
io.printLine(`Updated ${path}${backup}`);
|
|
208
|
+
if (update.warning)
|
|
209
|
+
io.printLine(`Warning: ${sanitizeTerminalText(update.warning)}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function withSkillUpdates(result, skills) {
|
|
213
|
+
return skills.updates === null ? result : { ...result, skillUpdates: skills.updates };
|
|
214
|
+
}
|
|
215
|
+
function emit(io, result) {
|
|
216
|
+
if (io.json)
|
|
217
|
+
io.printLine(JSON.stringify(result));
|
|
218
|
+
}
|
|
219
|
+
function fail(io, code, detail, result) {
|
|
220
|
+
io.printError(`${code}: ${detail}`);
|
|
221
|
+
emit(io, result);
|
|
222
|
+
return 1;
|
|
223
|
+
}
|
|
224
|
+
function selfUpdate(io, install, progress, stop) {
|
|
225
|
+
return Effect.gen(function* () {
|
|
226
|
+
progress.install = install;
|
|
227
|
+
const checked = yield* Effect.promise(async () => {
|
|
228
|
+
try {
|
|
229
|
+
const registry = resolveUpdateRegistry();
|
|
230
|
+
const latest = await resolveLatest(registry);
|
|
231
|
+
tryWriteCache(latest);
|
|
232
|
+
return { ok: true, registry, latest };
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
return { ok: false, error };
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
if (!checked.ok) {
|
|
239
|
+
const { error } = checked;
|
|
240
|
+
return fail(io, 'update_check_failed', sanitizeTerminalText(error instanceof Error ? error.message : error), {
|
|
241
|
+
installed: io.installed, latest: null, action: 'check-failed', prefix: install.prefix, skills: 'not-run',
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
const { latest, registry } = checked;
|
|
245
|
+
progress.latest = latest;
|
|
246
|
+
if (compareSemver(io.installed, latest) >= 0) {
|
|
247
|
+
if (!io.json)
|
|
248
|
+
io.printLine(`orizu ${io.installed} is already the latest.`);
|
|
249
|
+
if (io.dryRun) {
|
|
250
|
+
emit(io, { installed: io.installed, latest, action: 'already-latest', prefix: install.prefix, skills: 'not-run' });
|
|
251
|
+
return 0;
|
|
252
|
+
}
|
|
253
|
+
progress.step = 'refreshing-skills';
|
|
254
|
+
const skills = yield* refreshManagedSkills(install.binPath, [], io, stop);
|
|
255
|
+
progress.step = null;
|
|
256
|
+
if (skills.status !== 0) {
|
|
257
|
+
const detail = skills.timedOut
|
|
258
|
+
? 'orizu is already the latest, but its managed skills refresh timed out.'
|
|
259
|
+
: 'orizu is already the latest, but its managed skills could not be refreshed.';
|
|
260
|
+
return fail(io, 'update_skills_refresh_failed', detail, withSkillUpdates({
|
|
261
|
+
installed: io.installed, latest, action: 'already-latest', prefix: install.prefix, skills: 'refresh-failed',
|
|
262
|
+
}, skills));
|
|
263
|
+
}
|
|
264
|
+
emit(io, withSkillUpdates({ installed: io.installed, latest, action: 'already-latest', prefix: install.prefix, skills: skills.skills }, skills));
|
|
265
|
+
return 0;
|
|
266
|
+
}
|
|
267
|
+
if (io.dryRun) {
|
|
268
|
+
if (!io.json)
|
|
269
|
+
io.printLine(`Would update orizu ${io.installed} → ${latest} in ${install.prefix}.`);
|
|
270
|
+
emit(io, { installed: io.installed, latest, action: 'would-update', prefix: install.prefix, skills: 'would-refresh' });
|
|
271
|
+
return 0;
|
|
272
|
+
}
|
|
273
|
+
const npmCommand = npmInvocation();
|
|
274
|
+
const registryArgs = process.env.ORIZU_UPDATE_REGISTRY !== undefined
|
|
275
|
+
? ['--registry', registry]
|
|
276
|
+
: [];
|
|
277
|
+
progress.step = 'installing';
|
|
278
|
+
const npm = yield* runStreaming(npmCommand.command, [...npmCommand.args,
|
|
279
|
+
'install', '-g', `orizu@${latest}`, '--prefix', install.prefix, '--no-audit', '--no-fund',
|
|
280
|
+
...registryArgs,
|
|
281
|
+
], io, stop);
|
|
282
|
+
progress.step = null;
|
|
283
|
+
if (npm.status !== 0) {
|
|
284
|
+
const tail = npm.stderr.trim() ? ` npm stderr tail: ${npm.stderr.trim()}` : '';
|
|
285
|
+
return fail(io, 'update_install_failed', `${tail} Run \`${MANUAL_INSTALL_COMMAND}\`.`, {
|
|
286
|
+
installed: io.installed, latest, action: 'install-failed', prefix: install.prefix, skills: 'not-run',
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
const verifyCommand = cliInvocation(install, ['--version']);
|
|
290
|
+
const verificationTimeoutMs = verifyTimeoutMs();
|
|
291
|
+
progress.step = 'verifying';
|
|
292
|
+
const verified = yield* runCaptured(verifyCommand.command, verifyCommand.args, verificationTimeoutMs, stop);
|
|
293
|
+
progress.step = null;
|
|
294
|
+
const observed = sanitizeTerminalText(verified.stdout).trim();
|
|
295
|
+
if (verified.timedOut) {
|
|
296
|
+
return fail(io, 'update_verify_mismatch', `verification timed out after ${verificationTimeoutMs} ms.`, {
|
|
297
|
+
installed: io.installed, latest, action: 'verify-mismatch', prefix: install.prefix, skills: 'not-run',
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
if (verified.status !== 0 || observed !== `orizu ${latest}`) {
|
|
301
|
+
const launchError = verified.error ? ` launch error: ${sanitizeTerminalText(verified.error.message)}.` : '';
|
|
302
|
+
const exit = verified.status === null ? `signal ${verified.signal ?? 'unknown'}` : `exit ${verified.status}`;
|
|
303
|
+
const errorTail = sanitizeTerminalText(verified.stderr).trim().split('\n').slice(-3).join(' ');
|
|
304
|
+
const detail = `expected orizu ${latest}, observed ${observed || '<no version>'} (${exit}).${launchError}${errorTail ? ` ${errorTail}` : ''}`;
|
|
305
|
+
return fail(io, 'update_verify_mismatch', `${detail} Run \`${MANUAL_INSTALL_COMMAND}\` to reinstall.`, {
|
|
306
|
+
installed: io.installed, latest, action: 'verify-mismatch', prefix: install.prefix, skills: 'not-run',
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
progress.installedNow = latest;
|
|
310
|
+
if (!io.json)
|
|
311
|
+
io.printLine(`orizu ${latest} installed.`);
|
|
312
|
+
const skillsCommand = cliInvocation(install, []);
|
|
313
|
+
progress.step = 'refreshing-skills';
|
|
314
|
+
const skills = yield* refreshManagedSkills(skillsCommand.command, skillsCommand.args, io, stop);
|
|
315
|
+
progress.step = null;
|
|
316
|
+
if (skills.status !== 0) {
|
|
317
|
+
const detail = skills.timedOut
|
|
318
|
+
? 'the CLI was updated, but its managed skills refresh timed out.'
|
|
319
|
+
: 'the CLI was updated, but its managed skills could not be refreshed.';
|
|
320
|
+
return fail(io, 'update_skills_refresh_failed', detail, withSkillUpdates({
|
|
321
|
+
installed: io.installed, latest, action: 'updated', prefix: install.prefix, skills: 'refresh-failed',
|
|
322
|
+
}, skills));
|
|
323
|
+
}
|
|
324
|
+
emit(io, withSkillUpdates({ installed: io.installed, latest, action: 'updated', prefix: install.prefix, skills: skills.skills }, skills));
|
|
325
|
+
return 0;
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
// After a stop: which orizu the global install runs now, read once in the stop cap's last 5 s.
|
|
329
|
+
async function installedVersionAfterStop(install) {
|
|
330
|
+
const command = cliInvocation(install, ['--version']);
|
|
331
|
+
const checked = await Effect.runPromise(runCaptured(command.command, command.args, STOP_VERSION_CHECK_MS, null));
|
|
332
|
+
const match = /^orizu (\S+)$/u.exec(sanitizeTerminalText(checked.stdout).trim());
|
|
333
|
+
return checked.status === 0 && match ? match[1] : null;
|
|
334
|
+
}
|
|
335
|
+
async function reportStop(io, progress) {
|
|
336
|
+
const { step, install, latest } = progress;
|
|
337
|
+
if (!step || !install || !latest)
|
|
338
|
+
return;
|
|
339
|
+
if (step === 'refreshing-skills') {
|
|
340
|
+
io.printError(`update_stopped: stopped while refreshing the managed skills. orizu ${progress.installedNow} is installed now. Run \`orizu skills update\` to finish the refresh.`);
|
|
341
|
+
emit(io, { installed: io.installed, latest, action: 'stopped', prefix: install.prefix, skills: 'refresh-stopped' });
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const now = await installedVersionAfterStop(install);
|
|
345
|
+
const doing = step === 'installing' ? `npm was installing orizu ${latest}` : `checking the new orizu ${latest}`;
|
|
346
|
+
const state = now ? `orizu ${now} is installed now.` : 'Could not tell which orizu is installed now.';
|
|
347
|
+
io.printError(`update_stopped: stopped while ${doing}. ${state} Run \`${MANUAL_INSTALL_COMMAND}\` to finish the update.`);
|
|
348
|
+
emit(io, { installed: io.installed, latest, action: 'stopped', prefix: install.prefix, skills: 'not-run' });
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Runs the update with stop handlers installed for exactly its duration. A
|
|
352
|
+
* stop interrupts it: the running child gets the same signal and the stop cap
|
|
353
|
+
* to exit, the command says what is installed now, then ends by that signal.
|
|
354
|
+
*/
|
|
355
|
+
export async function runSelfUpdate(io, install) {
|
|
356
|
+
const progress = { step: null, install: null, latest: null, installedNow: io.installed };
|
|
357
|
+
const stop = { signal: null, deadline: 0 };
|
|
358
|
+
// eslint-disable-next-line prefer-const -- assigned after the handlers that read it
|
|
359
|
+
let fiber;
|
|
360
|
+
const handlers = STOP_SIGNALS.map(signal => ({
|
|
361
|
+
signal,
|
|
362
|
+
handle: () => {
|
|
363
|
+
if (stop.signal)
|
|
364
|
+
return;
|
|
365
|
+
stop.signal = signal;
|
|
366
|
+
stop.deadline = Date.now() + testMs('ORIZU_UPDATE_TEST_STOP_WAIT_MS', STOP_CHILD_WAIT_MS);
|
|
367
|
+
void Effect.runPromise(Fiber.interrupt(fiber));
|
|
368
|
+
},
|
|
369
|
+
}));
|
|
370
|
+
const removeHandlers = () => { for (const entry of handlers)
|
|
371
|
+
process.off(entry.signal, entry.handle); };
|
|
372
|
+
for (const entry of handlers)
|
|
373
|
+
process.on(entry.signal, entry.handle);
|
|
374
|
+
fiber = Effect.runFork(selfUpdate(io, install, progress, stop));
|
|
375
|
+
try {
|
|
376
|
+
const exit = await Effect.runPromise(Effect.exit(Fiber.join(fiber)));
|
|
377
|
+
if (stop.signal) {
|
|
378
|
+
await reportStop(io, progress);
|
|
379
|
+
removeHandlers();
|
|
380
|
+
process.kill(process.pid, stop.signal);
|
|
381
|
+
return await new Promise(() => { });
|
|
382
|
+
}
|
|
383
|
+
if (Exit.isSuccess(exit))
|
|
384
|
+
return exit.value;
|
|
385
|
+
throw Cause.squash(exit.cause);
|
|
386
|
+
}
|
|
387
|
+
finally {
|
|
388
|
+
removeHandlers();
|
|
389
|
+
}
|
|
390
|
+
}
|
package/dist/self-update.d.ts
CHANGED
|
@@ -1,25 +1,16 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
|
+
export declare const MANUAL_INSTALL_COMMAND = "npm i -g orizu@latest";
|
|
2
3
|
interface UpdateCache {
|
|
3
4
|
checkedAt: string;
|
|
4
5
|
latest: string | null;
|
|
5
6
|
}
|
|
6
|
-
interface GlobalInstall {
|
|
7
|
+
export interface GlobalInstall {
|
|
7
8
|
binPath: string;
|
|
8
9
|
prefix: string;
|
|
9
10
|
}
|
|
10
|
-
export interface
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
json: boolean;
|
|
14
|
-
refreshCache: boolean;
|
|
15
|
-
printLine(message?: string): void;
|
|
16
|
-
printError(message: string): void;
|
|
17
|
-
}
|
|
18
|
-
interface UpdateCommandIo {
|
|
19
|
-
getCliVersion(): string;
|
|
20
|
-
json: boolean;
|
|
21
|
-
printLine(message?: string): void;
|
|
22
|
-
printError(message: string): void;
|
|
11
|
+
export interface Invocation {
|
|
12
|
+
command: string;
|
|
13
|
+
args: string[];
|
|
23
14
|
}
|
|
24
15
|
export interface PassiveUpdateState {
|
|
25
16
|
cache: UpdateCache | null;
|
|
@@ -33,10 +24,32 @@ export interface PassiveUpdateIo {
|
|
|
33
24
|
export declare const defaultPassiveUpdateIo: PassiveUpdateIo;
|
|
34
25
|
export declare function compareSemver(left: string, right: string): number;
|
|
35
26
|
export declare function detectNpmGlobalInstall(entry?: string): GlobalInstall | null;
|
|
36
|
-
export declare function
|
|
37
|
-
export declare function
|
|
27
|
+
export declare function tryWriteCache(latest: string | null): void;
|
|
28
|
+
export declare function resolveUpdateRegistry(): string;
|
|
29
|
+
export declare function resolveLatest(registry?: string): Promise<string>;
|
|
30
|
+
export declare function npmInvocation(): Invocation;
|
|
31
|
+
export declare function cliInvocation(install: GlobalInstall, args: string[]): Invocation;
|
|
38
32
|
export declare function preparePassiveUpdate(rawArgs: string[], command: string | undefined, installed: string): PassiveUpdateState | null;
|
|
39
33
|
export declare function withPassiveUpdateNotice(rawArgs: string[], getInstalledVersion: () => string, printError: (message: string) => void, run: (args: string[]) => Promise<unknown>, io?: PassiveUpdateIo): void;
|
|
40
34
|
export declare function finishPassiveUpdate(state: PassiveUpdateState | null, printError: (message: string) => void, io?: PassiveUpdateIo): void;
|
|
35
|
+
export interface SelfUpdateIo {
|
|
36
|
+
dryRun: boolean;
|
|
37
|
+
installed: string;
|
|
38
|
+
json: boolean;
|
|
39
|
+
printLine(message?: string): void;
|
|
40
|
+
printError(message: string): void;
|
|
41
|
+
}
|
|
42
|
+
interface UpdateCommandIo {
|
|
43
|
+
getCliVersion(): string;
|
|
44
|
+
json: boolean;
|
|
45
|
+
printLine(message?: string): void;
|
|
46
|
+
printError(message: string): void;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* `orizu update`. Checking the arguments, the daily background cache refresh
|
|
50
|
+
* and refusing an install npm doesn't own stay plain code; only an update of
|
|
51
|
+
* an npm-global install loads the Effect workflow (ORI-2424).
|
|
52
|
+
*/
|
|
53
|
+
export declare function runUpdateCommand(rawArgs: string[], io: UpdateCommandIo): Promise<number>;
|
|
41
54
|
export {};
|
|
42
55
|
//# sourceMappingURL=self-update.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"self-update.d.ts","sourceRoot":"","sources":["../src/self-update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"self-update.d.ts","sourceRoot":"","sources":["../src/self-update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAuBrC,eAAO,MAAM,sBAAsB,0BAA0B,CAAA;AAI7D,UAAU,WAAW;IAAG,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE;AAClE,MAAM,WAAW,aAAa;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AAClE,MAAM,WAAW,UAAU;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE;AAE/D,MAAM,WAAW,kBAAkB;IAAG,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,aAAa,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE;AAC5G,MAAM,WAAW,eAAe;IAAG,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,KAAK,CAAA;CAAE;AAClF,eAAO,MAAM,sBAAsB,EAAE,eAA+C,CAAA;AAcpF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAsBjE;AAED,wBAAgB,sBAAsB,CAAC,KAAK,SAAkB,GAAG,aAAa,GAAG,IAAI,CAuBpF;AA2BD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAMzD;AA+CD,wBAAgB,qBAAqB,IAAI,MAAM,CAY9C;AAED,wBAAsB,aAAa,CAAC,QAAQ,SAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAUvF;AAED,wBAAgB,aAAa,IAAI,UAAU,CAI1C;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAEhF;AAOD,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,SAAS,EAAE,MAAM,GAChB,kBAAkB,GAAG,IAAI,CAa3B;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EAAE,EACjB,mBAAmB,EAAE,MAAM,MAAM,EACjC,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EACrC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,EACzC,EAAE,GAAE,eAAwC,GAC3C,IAAI,CAyBN;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,kBAAkB,GAAG,IAAI,EAChC,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EACrC,EAAE,GAAE,eAAwC,GAC3C,IAAI,CAoBN;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;IACjD,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACrE;AAED,UAAU,eAAe;IACvB,aAAa,IAAI,MAAM,CAAA;IACvB,IAAI,EAAE,OAAO,CAAA;IACb,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAyC9F"}
|