@senior-skills/node 1.0.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/dist/archive.d.ts +25 -0
- package/dist/archive.d.ts.map +1 -0
- package/dist/archive.js +101 -0
- package/dist/archive.js.map +1 -0
- package/dist/config-store.d.ts +27 -0
- package/dist/config-store.d.ts.map +1 -0
- package/dist/config-store.js +155 -0
- package/dist/config-store.js.map +1 -0
- package/dist/file-system.d.ts +40 -0
- package/dist/file-system.d.ts.map +1 -0
- package/dist/file-system.js +189 -0
- package/dist/file-system.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime.d.ts +51 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +215 -0
- package/dist/runtime.js.map +1 -0
- package/dist/selection-prompt.d.ts +21 -0
- package/dist/selection-prompt.d.ts.map +1 -0
- package/dist/selection-prompt.js +75 -0
- package/dist/selection-prompt.js.map +1 -0
- package/dist/tool-updater.d.ts +20 -0
- package/dist/tool-updater.d.ts.map +1 -0
- package/dist/tool-updater.js +96 -0
- package/dist/tool-updater.js.map +1 -0
- package/dist/update-state.d.ts +10 -0
- package/dist/update-state.d.ts.map +1 -0
- package/dist/update-state.js +70 -0
- package/dist/update-state.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type Clock, type CommandOptions, type CommandResult, type CommandRunner, type Environment, type Hasher, type HttpClient, type HttpRequestOptions, type HttpResponse, type LogLevel, type Logger } from '@senior-skills/core';
|
|
2
|
+
export declare class NodeEnvironment implements Environment {
|
|
3
|
+
homeDir(): string;
|
|
4
|
+
cwd(): string;
|
|
5
|
+
tempDir(): string;
|
|
6
|
+
platform(): string;
|
|
7
|
+
env(): Readonly<Record<string, string | undefined>>;
|
|
8
|
+
}
|
|
9
|
+
export declare class SystemClock implements Clock {
|
|
10
|
+
now(): Date;
|
|
11
|
+
}
|
|
12
|
+
export declare class NodeHasher implements Hasher {
|
|
13
|
+
hash(data: Uint8Array): string;
|
|
14
|
+
hashFile(target: string): Promise<string>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* HTTPS client built on `fetch`.
|
|
18
|
+
*
|
|
19
|
+
* Two policies are enforced here rather than at call sites: plaintext HTTP is refused
|
|
20
|
+
* outright (loopback excepted, for local development registries), and the response body is
|
|
21
|
+
* capped so a hostile server cannot exhaust memory before integrity checking gets a chance
|
|
22
|
+
* to run.
|
|
23
|
+
*/
|
|
24
|
+
export declare class NodeHttpClient implements HttpClient {
|
|
25
|
+
private readonly userAgent;
|
|
26
|
+
private readonly allowInsecure;
|
|
27
|
+
constructor(userAgent: string, allowInsecure?: boolean);
|
|
28
|
+
get(url: string, options?: HttpRequestOptions): Promise<HttpResponse>;
|
|
29
|
+
private assertTransport;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Runs external commands. Used only for `git` and for agent version probes.
|
|
33
|
+
*
|
|
34
|
+
* Arguments are always passed as an array and the shell is never involved, so a registry URL
|
|
35
|
+
* containing shell metacharacters cannot become a command injection.
|
|
36
|
+
*/
|
|
37
|
+
export declare class NodeCommandRunner implements CommandRunner {
|
|
38
|
+
run(command: string, args: readonly string[], options?: CommandOptions): Promise<CommandResult>;
|
|
39
|
+
which(command: string): Promise<string | undefined>;
|
|
40
|
+
}
|
|
41
|
+
/** Logs to stderr so `--json` output on stdout stays machine-parseable. */
|
|
42
|
+
export declare class ConsoleLogger implements Logger {
|
|
43
|
+
private readonly threshold;
|
|
44
|
+
constructor(level?: LogLevel);
|
|
45
|
+
debug(message: string, ...args: readonly unknown[]): void;
|
|
46
|
+
info(message: string, ...args: readonly unknown[]): void;
|
|
47
|
+
warn(message: string, ...args: readonly unknown[]): void;
|
|
48
|
+
error(message: string, ...args: readonly unknown[]): void;
|
|
49
|
+
private write;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAMA,OAAO,EAIL,KAAK,KAAK,EACV,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,MAAM,EACZ,MAAM,qBAAqB,CAAC;AAE7B,qBAAa,eAAgB,YAAW,WAAW;IACjD,OAAO,IAAI,MAAM;IAIjB,GAAG,IAAI,MAAM;IAIb,OAAO,IAAI,MAAM;IAIjB,QAAQ,IAAI,MAAM;IAIlB,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAGpD;AAED,qBAAa,WAAY,YAAW,KAAK;IACvC,GAAG,IAAI,IAAI;CAGZ;AAED,qBAAa,UAAW,YAAW,MAAM;IACvC,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM;IAIxB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAKhD;AAKD;;;;;;;GAOG;AACH,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;gBAE5B,SAAS,EAAE,MAAM,EAAE,aAAa,UAAQ;IAK9C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,YAAY,CAAC;IAuD/E,OAAO,CAAC,eAAe;CA2BxB;AAED;;;;;GAKG;AACH,qBAAa,iBAAkB,YAAW,aAAa;IAC/C,GAAG,CACP,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,aAAa,CAAC;IAsDnB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;CAmB1D;AAID,2EAA2E;AAC3E,qBAAa,aAAc,YAAW,MAAM;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,KAAK,GAAE,QAAiB;IAIpC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI;IAIzD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI;IAIxD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI;IAIxD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI;IAIzD,OAAO,CAAC,KAAK;CAMd"}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { createReadStream } from 'node:fs';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import { access, constants } from 'node:fs/promises';
|
|
7
|
+
import { AgentSkillsError, ErrorCode, INTEGRITY_PREFIX, } from '@senior-skills/core';
|
|
8
|
+
export class NodeEnvironment {
|
|
9
|
+
homeDir() {
|
|
10
|
+
return os.homedir();
|
|
11
|
+
}
|
|
12
|
+
cwd() {
|
|
13
|
+
return process.cwd();
|
|
14
|
+
}
|
|
15
|
+
tempDir() {
|
|
16
|
+
return os.tmpdir();
|
|
17
|
+
}
|
|
18
|
+
platform() {
|
|
19
|
+
return process.platform;
|
|
20
|
+
}
|
|
21
|
+
env() {
|
|
22
|
+
return process.env;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class SystemClock {
|
|
26
|
+
now() {
|
|
27
|
+
return new Date();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export class NodeHasher {
|
|
31
|
+
hash(data) {
|
|
32
|
+
return INTEGRITY_PREFIX + createHash('sha256').update(data).digest('base64');
|
|
33
|
+
}
|
|
34
|
+
async hashFile(target) {
|
|
35
|
+
const digest = createHash('sha256');
|
|
36
|
+
for await (const chunk of createReadStream(target))
|
|
37
|
+
digest.update(chunk);
|
|
38
|
+
return INTEGRITY_PREFIX + digest.digest('base64');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
42
|
+
const DEFAULT_MAX_BYTES = 64 * 1024 * 1024;
|
|
43
|
+
/**
|
|
44
|
+
* HTTPS client built on `fetch`.
|
|
45
|
+
*
|
|
46
|
+
* Two policies are enforced here rather than at call sites: plaintext HTTP is refused
|
|
47
|
+
* outright (loopback excepted, for local development registries), and the response body is
|
|
48
|
+
* capped so a hostile server cannot exhaust memory before integrity checking gets a chance
|
|
49
|
+
* to run.
|
|
50
|
+
*/
|
|
51
|
+
export class NodeHttpClient {
|
|
52
|
+
userAgent;
|
|
53
|
+
allowInsecure;
|
|
54
|
+
constructor(userAgent, allowInsecure = false) {
|
|
55
|
+
this.userAgent = userAgent;
|
|
56
|
+
this.allowInsecure = allowInsecure;
|
|
57
|
+
}
|
|
58
|
+
async get(url, options = {}) {
|
|
59
|
+
this.assertTransport(url);
|
|
60
|
+
const controller = new AbortController();
|
|
61
|
+
const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
62
|
+
try {
|
|
63
|
+
const response = await fetch(url, {
|
|
64
|
+
signal: controller.signal,
|
|
65
|
+
redirect: 'follow',
|
|
66
|
+
headers: { 'user-agent': this.userAgent, ...(options.headers ?? {}) },
|
|
67
|
+
});
|
|
68
|
+
const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
69
|
+
const declared = Number(response.headers.get('content-length') ?? '0');
|
|
70
|
+
if (declared > maxBytes) {
|
|
71
|
+
throw new AgentSkillsError(ErrorCode.UNSAFE_ARCHIVE, `Response from ${url} declares ${declared} bytes, over the ${maxBytes} byte limit`, { data: { url, declared, maxBytes } });
|
|
72
|
+
}
|
|
73
|
+
const body = new Uint8Array(await response.arrayBuffer());
|
|
74
|
+
if (body.byteLength > maxBytes) {
|
|
75
|
+
throw new AgentSkillsError(ErrorCode.UNSAFE_ARCHIVE, `Response from ${url} is ${body.byteLength} bytes, over the ${maxBytes} byte limit`, { data: { url, size: body.byteLength, maxBytes } });
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
status: response.status,
|
|
79
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
80
|
+
body,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (cause) {
|
|
84
|
+
if (cause instanceof AgentSkillsError)
|
|
85
|
+
throw cause;
|
|
86
|
+
const aborted = cause instanceof Error && cause.name === 'AbortError';
|
|
87
|
+
throw new AgentSkillsError(ErrorCode.REGISTRY_UNAVAILABLE, aborted ? `Request to ${url} timed out` : `Request to ${url} failed`, {
|
|
88
|
+
details: aborted ? [] : [cause instanceof Error ? cause.message : String(cause)],
|
|
89
|
+
hints: ['Check your network connection and any proxy settings'],
|
|
90
|
+
cause,
|
|
91
|
+
data: { url },
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
clearTimeout(timeout);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
assertTransport(url) {
|
|
99
|
+
let parsed;
|
|
100
|
+
try {
|
|
101
|
+
parsed = new URL(url);
|
|
102
|
+
}
|
|
103
|
+
catch (cause) {
|
|
104
|
+
throw new AgentSkillsError(ErrorCode.USAGE, `Invalid URL: ${url}`, { cause, data: { url } });
|
|
105
|
+
}
|
|
106
|
+
if (parsed.protocol === 'https:')
|
|
107
|
+
return;
|
|
108
|
+
const loopback = parsed.hostname === 'localhost' ||
|
|
109
|
+
parsed.hostname === '127.0.0.1' ||
|
|
110
|
+
parsed.hostname === '::1';
|
|
111
|
+
if (parsed.protocol === 'http:' && (this.allowInsecure || loopback))
|
|
112
|
+
return;
|
|
113
|
+
throw new AgentSkillsError(ErrorCode.INSECURE_TRANSPORT, `Refusing to fetch over ${parsed.protocol}//`, {
|
|
114
|
+
details: [url, '', 'Skill packages become instructions your coding agent follows.'],
|
|
115
|
+
hints: ['Use an https:// URL', 'For a local development registry, use http://localhost'],
|
|
116
|
+
data: { url, protocol: parsed.protocol },
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Runs external commands. Used only for `git` and for agent version probes.
|
|
122
|
+
*
|
|
123
|
+
* Arguments are always passed as an array and the shell is never involved, so a registry URL
|
|
124
|
+
* containing shell metacharacters cannot become a command injection.
|
|
125
|
+
*/
|
|
126
|
+
export class NodeCommandRunner {
|
|
127
|
+
async run(command, args, options = {}) {
|
|
128
|
+
return new Promise((resolve, reject) => {
|
|
129
|
+
const child = spawn(command, [...args], {
|
|
130
|
+
cwd: options.cwd ?? process.cwd(),
|
|
131
|
+
env: { ...process.env, ...(options.env ?? {}) },
|
|
132
|
+
shell: false,
|
|
133
|
+
windowsHide: true,
|
|
134
|
+
});
|
|
135
|
+
let stdout = '';
|
|
136
|
+
let stderr = '';
|
|
137
|
+
let settled = false;
|
|
138
|
+
const timer = setTimeout(() => {
|
|
139
|
+
if (settled)
|
|
140
|
+
return;
|
|
141
|
+
settled = true;
|
|
142
|
+
child.kill('SIGKILL');
|
|
143
|
+
reject(new AgentSkillsError(ErrorCode.IO_ERROR, `Command timed out: ${command} ${args.join(' ')}`, {
|
|
144
|
+
data: { command, args },
|
|
145
|
+
}));
|
|
146
|
+
}, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
147
|
+
child.stdout?.on('data', (chunk) => (stdout += String(chunk)));
|
|
148
|
+
child.stderr?.on('data', (chunk) => (stderr += String(chunk)));
|
|
149
|
+
child.on('error', (cause) => {
|
|
150
|
+
if (settled)
|
|
151
|
+
return;
|
|
152
|
+
settled = true;
|
|
153
|
+
clearTimeout(timer);
|
|
154
|
+
reject(new AgentSkillsError(ErrorCode.IO_ERROR, `Could not run "${command}"`, {
|
|
155
|
+
details: [cause.message],
|
|
156
|
+
hints: command === 'git' ? ['Install git, or use an http registry instead'] : [],
|
|
157
|
+
cause,
|
|
158
|
+
data: { command, args },
|
|
159
|
+
}));
|
|
160
|
+
});
|
|
161
|
+
child.on('close', (code) => {
|
|
162
|
+
if (settled)
|
|
163
|
+
return;
|
|
164
|
+
settled = true;
|
|
165
|
+
clearTimeout(timer);
|
|
166
|
+
resolve({ code: code ?? 0, stdout, stderr });
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
async which(command) {
|
|
171
|
+
const isWindows = process.platform === 'win32';
|
|
172
|
+
const extensions = isWindows
|
|
173
|
+
? (process.env['PATHEXT'] ?? '.EXE;.CMD;.BAT;.COM').split(';').filter(Boolean)
|
|
174
|
+
: [''];
|
|
175
|
+
for (const directory of (process.env['PATH'] ?? '').split(path.delimiter).filter(Boolean)) {
|
|
176
|
+
for (const extension of extensions) {
|
|
177
|
+
const candidate = path.join(directory, command + extension);
|
|
178
|
+
try {
|
|
179
|
+
await access(candidate, isWindows ? constants.F_OK : constants.X_OK);
|
|
180
|
+
return candidate;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
// Try the next candidate.
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const LEVEL_ORDER = { debug: 10, info: 20, warn: 30, error: 40 };
|
|
191
|
+
/** Logs to stderr so `--json` output on stdout stays machine-parseable. */
|
|
192
|
+
export class ConsoleLogger {
|
|
193
|
+
threshold;
|
|
194
|
+
constructor(level = 'info') {
|
|
195
|
+
this.threshold = LEVEL_ORDER[level];
|
|
196
|
+
}
|
|
197
|
+
debug(message, ...args) {
|
|
198
|
+
this.write('debug', message, args);
|
|
199
|
+
}
|
|
200
|
+
info(message, ...args) {
|
|
201
|
+
this.write('info', message, args);
|
|
202
|
+
}
|
|
203
|
+
warn(message, ...args) {
|
|
204
|
+
this.write('warn', message, args);
|
|
205
|
+
}
|
|
206
|
+
error(message, ...args) {
|
|
207
|
+
this.write('error', message, args);
|
|
208
|
+
}
|
|
209
|
+
write(level, message, args) {
|
|
210
|
+
if (LEVEL_ORDER[level] < this.threshold)
|
|
211
|
+
return;
|
|
212
|
+
process.stderr.write(`${level === 'info' ? '' : `${level}: `}${message}${args.length > 0 ? ` ${args.map(String).join(' ')}` : ''}\n`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Readable, Writable } from 'node:stream';
|
|
2
|
+
import type { SelectionPrompt } from '@senior-skills/core';
|
|
3
|
+
export type PromptInput = Readable & {
|
|
4
|
+
isTTY?: boolean;
|
|
5
|
+
isRaw?: boolean;
|
|
6
|
+
setRawMode(mode: boolean): unknown;
|
|
7
|
+
ref?(): unknown;
|
|
8
|
+
unref?(): unknown;
|
|
9
|
+
};
|
|
10
|
+
export type PromptOutput = Writable & {
|
|
11
|
+
isTTY?: boolean;
|
|
12
|
+
columns?: number;
|
|
13
|
+
};
|
|
14
|
+
/** A small terminal menu: arrows, digits, Enter, and Escape; no shell or agent process. */
|
|
15
|
+
export declare class NodeSelectionPrompt implements SelectionPrompt {
|
|
16
|
+
private readonly input;
|
|
17
|
+
private readonly output;
|
|
18
|
+
constructor(input?: PromptInput, output?: PromptOutput);
|
|
19
|
+
choose(message: string, choices: readonly string[]): Promise<number | undefined>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=selection-prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection-prompt.d.ts","sourceRoot":"","sources":["../src/selection-prompt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACnC,GAAG,CAAC,IAAI,OAAO,CAAC;IAChB,KAAK,CAAC,IAAI,OAAO,CAAC;CACnB,CAAC;AACF,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,2FAA2F;AAC3F,qBAAa,mBAAoB,YAAW,eAAe;IACzD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;gBAE1B,KAAK,GAAE,WAA2B,EAAE,MAAM,GAAE,YAA6B;IAK/E,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;CA+DvF"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { emitKeypressEvents } from 'node:readline';
|
|
2
|
+
/** A small terminal menu: arrows, digits, Enter, and Escape; no shell or agent process. */
|
|
3
|
+
export class NodeSelectionPrompt {
|
|
4
|
+
input;
|
|
5
|
+
output;
|
|
6
|
+
constructor(input = process.stdin, output = process.stderr) {
|
|
7
|
+
this.input = input;
|
|
8
|
+
this.output = output;
|
|
9
|
+
}
|
|
10
|
+
async choose(message, choices) {
|
|
11
|
+
if (!this.input.isTTY || !this.output.isTTY || choices.length === 0)
|
|
12
|
+
return undefined;
|
|
13
|
+
const input = this.input;
|
|
14
|
+
const output = this.output;
|
|
15
|
+
const wasRaw = input.isRaw === true;
|
|
16
|
+
const wasFlowing = input.readableFlowing === true;
|
|
17
|
+
let selected = 0;
|
|
18
|
+
output.write(`\n${message}\n\n`);
|
|
19
|
+
// Keep each redrawn menu row on one physical line, including on narrow terminals.
|
|
20
|
+
const fit = (text) => {
|
|
21
|
+
const width = Math.max(10, (output.columns || 80) - 1);
|
|
22
|
+
return text.length <= width ? text : `${text.slice(0, width - 3)}...`;
|
|
23
|
+
};
|
|
24
|
+
const draw = () => {
|
|
25
|
+
choices.forEach((choice, index) => output.write(`\x1b[2K\r${fit(`${index === selected ? '>' : ' '} ${index + 1}. ${choice}`)}\n`));
|
|
26
|
+
output.write(`\x1b[2K\r${fit('Use arrows or a number, Enter to select, Esc to continue.')}`);
|
|
27
|
+
};
|
|
28
|
+
emitKeypressEvents(input);
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
let finished = false;
|
|
31
|
+
const finish = (value) => {
|
|
32
|
+
if (finished)
|
|
33
|
+
return;
|
|
34
|
+
finished = true;
|
|
35
|
+
input.off('keypress', onKey);
|
|
36
|
+
input.off('end', onEnd);
|
|
37
|
+
input.off('close', onEnd);
|
|
38
|
+
input.off('error', onEnd);
|
|
39
|
+
input.setRawMode(wasRaw);
|
|
40
|
+
if (!wasFlowing) {
|
|
41
|
+
input.pause();
|
|
42
|
+
input.unref?.();
|
|
43
|
+
}
|
|
44
|
+
output.write('\n');
|
|
45
|
+
resolve(value);
|
|
46
|
+
};
|
|
47
|
+
const onEnd = () => finish();
|
|
48
|
+
const onKey = (_text, key) => {
|
|
49
|
+
if (key.name === 'escape' || (key.ctrl && ['c', 'd'].includes(key.name ?? '')))
|
|
50
|
+
return finish();
|
|
51
|
+
if (key.name === 'return' || key.name === 'enter')
|
|
52
|
+
return finish(selected);
|
|
53
|
+
if (key.name === 'up')
|
|
54
|
+
selected = (selected + choices.length - 1) % choices.length;
|
|
55
|
+
else if (key.name === 'down' || key.name === 'tab')
|
|
56
|
+
selected = (selected + 1) % choices.length;
|
|
57
|
+
else if (/^[1-9]$/.test(key.sequence ?? '') && Number(key.sequence) <= choices.length)
|
|
58
|
+
selected = Number(key.sequence) - 1;
|
|
59
|
+
else
|
|
60
|
+
return;
|
|
61
|
+
output.write(`\r\x1b[${choices.length}A`);
|
|
62
|
+
draw();
|
|
63
|
+
};
|
|
64
|
+
input.on('keypress', onKey);
|
|
65
|
+
input.once('end', onEnd);
|
|
66
|
+
input.once('close', onEnd);
|
|
67
|
+
input.once('error', onEnd);
|
|
68
|
+
input.setRawMode(true);
|
|
69
|
+
input.ref?.();
|
|
70
|
+
input.resume();
|
|
71
|
+
draw();
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=selection-prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection-prompt.js","sourceRoot":"","sources":["../src/selection-prompt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type CommandRunner, type Environment, type FileSystem, type HttpClient, type SemanticVersion, type ToolUpdateAction, type ToolUpdater } from '@senior-skills/core';
|
|
2
|
+
export declare const TOOL_PACKAGE = "@senior-skills/cli";
|
|
3
|
+
export declare const TOOL_VERSION_URL = "https://registry.npmjs.org/@senior-skills%2fcli/latest";
|
|
4
|
+
interface Options {
|
|
5
|
+
readonly fs: FileSystem;
|
|
6
|
+
readonly env: Environment;
|
|
7
|
+
readonly commands: CommandRunner;
|
|
8
|
+
readonly http: HttpClient;
|
|
9
|
+
readonly packageDirectory: string;
|
|
10
|
+
readonly nodeExecutable: string;
|
|
11
|
+
}
|
|
12
|
+
/** npm metadata is data only. Neither commands nor executable paths come from the response. */
|
|
13
|
+
export declare class NpmToolUpdater implements ToolUpdater {
|
|
14
|
+
private readonly options;
|
|
15
|
+
constructor(options: Options);
|
|
16
|
+
latestVersion(): Promise<SemanticVersion | undefined>;
|
|
17
|
+
action(version: SemanticVersion): Promise<ToolUpdateAction>;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=tool-updater.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-updater.d.ts","sourceRoot":"","sources":["../src/tool-updater.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,qBAAqB,CAAC;AAE7B,eAAO,MAAM,YAAY,uBAAuB,CAAC;AACjD,eAAO,MAAM,gBAAgB,2DAA2D,CAAC;AAEzF,UAAU,OAAO;IACf,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,+FAA+F;AAC/F,qBAAa,cAAe,YAAW,WAAW;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;gBAEtB,OAAO,EAAE,OAAO;IAItB,aAAa,IAAI,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAmBrD,MAAM,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAkElE"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { AgentSkillsError, ErrorCode, isSemanticVersion, parseVersion, } from '@senior-skills/core';
|
|
2
|
+
export const TOOL_PACKAGE = '@senior-skills/cli';
|
|
3
|
+
export const TOOL_VERSION_URL = 'https://registry.npmjs.org/@senior-skills%2fcli/latest';
|
|
4
|
+
/** npm metadata is data only. Neither commands nor executable paths come from the response. */
|
|
5
|
+
export class NpmToolUpdater {
|
|
6
|
+
options;
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.options = options;
|
|
9
|
+
}
|
|
10
|
+
async latestVersion() {
|
|
11
|
+
const response = await this.options.http.get(TOOL_VERSION_URL, {
|
|
12
|
+
timeoutMs: 2000,
|
|
13
|
+
maxBytes: 256 * 1024,
|
|
14
|
+
});
|
|
15
|
+
if (response.status !== 200)
|
|
16
|
+
return undefined;
|
|
17
|
+
const raw = JSON.parse(new TextDecoder().decode(response.body));
|
|
18
|
+
if (raw === null || typeof raw !== 'object')
|
|
19
|
+
return undefined;
|
|
20
|
+
const version = raw['version'];
|
|
21
|
+
// Automatic notices target stable releases, even if a publisher mis-tags a prerelease.
|
|
22
|
+
if (typeof version !== 'string' ||
|
|
23
|
+
!/^\d+\.\d+\.\d+(?:\+[\w.-]+)?$/.test(version) ||
|
|
24
|
+
!isSemanticVersion(version))
|
|
25
|
+
return undefined;
|
|
26
|
+
return parseVersion(version);
|
|
27
|
+
}
|
|
28
|
+
async action(version) {
|
|
29
|
+
// Revalidate even cached metadata at the execution boundary.
|
|
30
|
+
if (!/^\d+\.\d+\.\d+(?:\+[\w.-]+)?$/.test(version) || !isSemanticVersion(version)) {
|
|
31
|
+
throw new AgentSkillsError(ErrorCode.INVALID_VERSION, 'Invalid CLI update version');
|
|
32
|
+
}
|
|
33
|
+
const { fs, env, commands, packageDirectory, nodeExecutable } = this.options;
|
|
34
|
+
const directory = await fs.realpath(packageDirectory);
|
|
35
|
+
const normalized = directory.replace(/\\/g, '/');
|
|
36
|
+
if (normalized.includes('/_npx/'))
|
|
37
|
+
return { label: `npx ${TOOL_PACKAGE}@${version} <command>` };
|
|
38
|
+
const manual = {
|
|
39
|
+
label: 'Update this installation with its original package manager, then restart senior-skills.',
|
|
40
|
+
};
|
|
41
|
+
if (fs.basename(directory) !== 'cli' || fs.basename(fs.dirname(directory)) !== '@senior-skills')
|
|
42
|
+
return manual;
|
|
43
|
+
const modules = fs.dirname(fs.dirname(directory));
|
|
44
|
+
if (fs.basename(modules) !== 'node_modules' || /\/(?:\.pnpm|\.yarn|\.bun)\//.test(normalized))
|
|
45
|
+
return manual;
|
|
46
|
+
const npm = await commands.which('npm');
|
|
47
|
+
if (npm === undefined)
|
|
48
|
+
return manual;
|
|
49
|
+
let executable = npm;
|
|
50
|
+
let prefix = [];
|
|
51
|
+
const realNpm = await fs.realpath(npm);
|
|
52
|
+
const script = /\.(?:cmd|bat)$/i.test(npm)
|
|
53
|
+
? fs.join(fs.dirname(npm), 'node_modules', 'npm', 'bin', 'npm-cli.js')
|
|
54
|
+
: realNpm;
|
|
55
|
+
if (script.endsWith('.js') && (await fs.exists(script))) {
|
|
56
|
+
executable = nodeExecutable;
|
|
57
|
+
prefix = [script];
|
|
58
|
+
}
|
|
59
|
+
else if (/\.(?:cmd|bat)$/i.test(npm))
|
|
60
|
+
return manual;
|
|
61
|
+
const root = await commands.run(executable, [...prefix, 'root', '--global'], {
|
|
62
|
+
cwd: env.homeDir(),
|
|
63
|
+
timeoutMs: 2000,
|
|
64
|
+
});
|
|
65
|
+
const globalRoot = root.stdout.trim();
|
|
66
|
+
const isGlobal = root.code === 0 &&
|
|
67
|
+
fs.isAbsolute(globalRoot) &&
|
|
68
|
+
(await fs.exists(globalRoot)) &&
|
|
69
|
+
(await fs.realpath(globalRoot)) === (await fs.realpath(modules));
|
|
70
|
+
const cwd = isGlobal ? env.homeDir() : fs.dirname(modules);
|
|
71
|
+
let saveFlag = [];
|
|
72
|
+
if (!isGlobal) {
|
|
73
|
+
// A local update may edit a manifest/lockfile. Require evidence that npm owns it and
|
|
74
|
+
// that the CLI is a direct dependency, rather than converting a transitive install.
|
|
75
|
+
for (const lock of ['pnpm-lock.yaml', 'yarn.lock', 'bun.lock', 'bun.lockb']) {
|
|
76
|
+
if (await fs.exists(fs.join(cwd, lock)))
|
|
77
|
+
return manual;
|
|
78
|
+
}
|
|
79
|
+
if (!(await fs.exists(fs.join(cwd, 'package-lock.json'))))
|
|
80
|
+
return manual;
|
|
81
|
+
const pkg = JSON.parse(await fs.readTextFile(fs.join(cwd, 'package.json')));
|
|
82
|
+
if (typeof pkg['devDependencies']?.[TOOL_PACKAGE] === 'string')
|
|
83
|
+
saveFlag = ['--save-dev'];
|
|
84
|
+
else if (typeof pkg['optionalDependencies']?.[TOOL_PACKAGE] === 'string')
|
|
85
|
+
saveFlag = ['--save-optional'];
|
|
86
|
+
else if (typeof pkg['dependencies']?.[TOOL_PACKAGE] !== 'string')
|
|
87
|
+
return manual;
|
|
88
|
+
}
|
|
89
|
+
const args = ['install', ...(isGlobal ? ['--global'] : saveFlag), `${TOOL_PACKAGE}@${version}`];
|
|
90
|
+
return {
|
|
91
|
+
label: `npm ${args.join(' ')}${isGlobal ? '' : ` (in ${cwd})`}`,
|
|
92
|
+
run: () => commands.run(executable, [...prefix, ...args], { cwd, timeoutMs: 300_000 }),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=tool-updater.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-updater.js","sourceRoot":"","sources":["../src/tool-updater.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Environment, type FileSystem, type UpdateState, type UpdateStateStore } from '@senior-skills/core';
|
|
2
|
+
/** Corrupt or newer advisory state is discarded; it must never break a CLI command. */
|
|
3
|
+
export declare class FileUpdateStateStore implements UpdateStateStore {
|
|
4
|
+
private readonly fs;
|
|
5
|
+
private readonly path;
|
|
6
|
+
constructor(fs: FileSystem, env: Environment);
|
|
7
|
+
load(): Promise<UpdateState>;
|
|
8
|
+
save(state: UpdateState): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=update-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-state.d.ts","sourceRoot":"","sources":["../src/update-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,UAAU,EAEf,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAC;AAG7B,uFAAuF;AACvF,qBAAa,oBAAqB,YAAW,gBAAgB;IAC3D,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAa;IAChC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;gBAElB,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW;IAKtC,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC;IAuC5B,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAY9C"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { emptyUpdateState, isSemanticVersion, } from '@senior-skills/core';
|
|
2
|
+
import { configHome } from "./config-store.js";
|
|
3
|
+
/** Corrupt or newer advisory state is discarded; it must never break a CLI command. */
|
|
4
|
+
export class FileUpdateStateStore {
|
|
5
|
+
fs;
|
|
6
|
+
path;
|
|
7
|
+
constructor(fs, env) {
|
|
8
|
+
this.fs = fs;
|
|
9
|
+
this.path = fs.join(configHome(fs, env), 'updates.json');
|
|
10
|
+
}
|
|
11
|
+
async load() {
|
|
12
|
+
try {
|
|
13
|
+
if ((await this.fs.lstat(this.path)).size > 1024 * 1024)
|
|
14
|
+
return emptyUpdateState();
|
|
15
|
+
const raw = JSON.parse(await this.fs.readTextFile(this.path));
|
|
16
|
+
if (!record(raw) || raw['schemaVersion'] !== 1)
|
|
17
|
+
return emptyUpdateState();
|
|
18
|
+
const checks = {};
|
|
19
|
+
if (record(raw['checks'])) {
|
|
20
|
+
for (const [key, value] of Object.entries(raw['checks']).slice(-1000)) {
|
|
21
|
+
if (!record(value) || !timestamp(value['checkedAt']) || !Array.isArray(value['versions']))
|
|
22
|
+
continue;
|
|
23
|
+
const versions = value['versions'];
|
|
24
|
+
if (!versions.every((v) => typeof v === 'string' && isSemanticVersion(v)))
|
|
25
|
+
continue;
|
|
26
|
+
Object.defineProperty(checks, key, {
|
|
27
|
+
value: { checkedAt: value['checkedAt'], versions: versions },
|
|
28
|
+
enumerable: true,
|
|
29
|
+
writable: true,
|
|
30
|
+
configurable: true,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const dismissed = Array.isArray(raw['dismissed'])
|
|
35
|
+
? raw['dismissed']
|
|
36
|
+
.filter((id) => typeof id === 'string')
|
|
37
|
+
.slice(-1000)
|
|
38
|
+
: [];
|
|
39
|
+
const deferred = record(raw['deferred'])
|
|
40
|
+
? Object.fromEntries(Object.entries(raw['deferred'])
|
|
41
|
+
.filter((entry) => timestamp(entry[1]))
|
|
42
|
+
.slice(-1000))
|
|
43
|
+
: {};
|
|
44
|
+
return { checks, dismissed, deferred };
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return emptyUpdateState();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async save(state) {
|
|
51
|
+
const parent = this.fs.dirname(this.path);
|
|
52
|
+
await this.fs.mkdirp(parent);
|
|
53
|
+
const staging = await this.fs.makeTempDir(parent, '.updates-');
|
|
54
|
+
try {
|
|
55
|
+
const file = this.fs.join(staging, 'updates.json');
|
|
56
|
+
await this.fs.writeFile(file, `${JSON.stringify({ schemaVersion: 1, ...state })}\n`);
|
|
57
|
+
await this.fs.rename(file, this.path);
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
await this.fs.remove(staging);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function record(value) {
|
|
65
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
66
|
+
}
|
|
67
|
+
function timestamp(value) {
|
|
68
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=update-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-state.js","sourceRoot":"","sources":["../src/update-state.ts"],"names":[],"mappings":"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"}
|