@oxygen-agent/cli 1.50.37 → 1.98.7
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 +1 -1
- package/dist/browser-login.js +1 -1
- package/dist/credentials.d.ts +33 -0
- package/dist/credentials.js +200 -7
- package/dist/http-client.d.ts +5 -1
- package/dist/http-client.js +23 -2
- package/dist/index.js +2123 -366
- package/dist/skills.d.ts +47 -0
- package/dist/skills.js +375 -0
- package/dist/update.d.ts +3 -0
- package/dist/update.js +9 -5
- package/dist/windows-shim.d.ts +7 -0
- package/dist/windows-shim.js +21 -0
- package/node_modules/@oxygen/shared/dist/cell-format.js +2 -3
- package/node_modules/@oxygen/shared/dist/file-import.d.ts +5 -0
- package/node_modules/@oxygen/shared/dist/file-import.js +156 -6
- package/node_modules/@oxygen/shared/dist/index.d.ts +1 -0
- package/node_modules/@oxygen/shared/dist/index.js +1 -0
- package/node_modules/@oxygen/shared/dist/object-storage.d.ts +26 -0
- package/node_modules/@oxygen/shared/dist/object-storage.js +115 -0
- package/node_modules/@oxygen/shared/dist/provider-request-outcomes.d.ts +3 -0
- package/node_modules/@oxygen/shared/dist/provider-request-outcomes.js +5 -0
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +1 -1
- package/node_modules/@oxygen/workflows/dist/index.d.ts +91 -0
- package/node_modules/@oxygen/workflows/dist/index.js +268 -4
- package/package.json +1 -1
package/dist/skills.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
2
|
+
export type ExecFileSyncLike = typeof execFileSync;
|
|
3
|
+
export type SpawnSyncLike = typeof spawnSync;
|
|
4
|
+
export type SkillsInstallOptions = {
|
|
5
|
+
json?: boolean;
|
|
6
|
+
apiUrl?: string;
|
|
7
|
+
agents?: string | string[];
|
|
8
|
+
skill?: string;
|
|
9
|
+
project?: boolean;
|
|
10
|
+
copy?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export type SkillsListOptions = {
|
|
13
|
+
json?: boolean;
|
|
14
|
+
apiUrl?: string;
|
|
15
|
+
};
|
|
16
|
+
export type SkillsDoctorOptions = {
|
|
17
|
+
json?: boolean;
|
|
18
|
+
apiUrl?: string;
|
|
19
|
+
};
|
|
20
|
+
export type AutomaticSkillsInstallResult = {
|
|
21
|
+
attempted: boolean;
|
|
22
|
+
ok: boolean;
|
|
23
|
+
command: string;
|
|
24
|
+
reason?: string;
|
|
25
|
+
error?: {
|
|
26
|
+
code: string;
|
|
27
|
+
message: string;
|
|
28
|
+
details?: unknown;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export declare function listAgentSkills(options: SkillsListOptions): Promise<Record<string, unknown>>;
|
|
32
|
+
export declare function doctorAgentSkills(options: SkillsDoctorOptions): Promise<Record<string, unknown>>;
|
|
33
|
+
export declare function installAgentSkills(// skipcq: JS-R1005
|
|
34
|
+
options: SkillsInstallOptions, runtime?: {
|
|
35
|
+
env?: NodeJS.ProcessEnv;
|
|
36
|
+
platform?: NodeJS.Platform;
|
|
37
|
+
execFileSync?: ExecFileSyncLike;
|
|
38
|
+
spawnSync?: SpawnSyncLike;
|
|
39
|
+
}): Record<string, unknown>;
|
|
40
|
+
export declare function runAutomaticSkillsInstall(options?: {
|
|
41
|
+
apiUrl?: string;
|
|
42
|
+
env?: NodeJS.ProcessEnv;
|
|
43
|
+
platform?: NodeJS.Platform;
|
|
44
|
+
execFileSync?: ExecFileSyncLike;
|
|
45
|
+
spawnSync?: SpawnSyncLike;
|
|
46
|
+
}): AutomaticSkillsInstallResult;
|
|
47
|
+
export declare function skippedAutomaticSkillsInstall(reason: string, apiUrl?: string): AutomaticSkillsInstallResult;
|
package/dist/skills.js
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
import { OxygenError, toFailure } from "@oxygen/shared";
|
|
6
|
+
import { defaultApiUrl, normalizeApiUrl } from "./credentials.js";
|
|
7
|
+
import { resolveCmdShimInvocation } from "./windows-shim.js";
|
|
8
|
+
const DEFAULT_SKILL_AGENTS = ["codex", "claude-code", "cursor"];
|
|
9
|
+
const SKILL_INDEX_PATH = "/.well-known/skills/index.json";
|
|
10
|
+
export async function listAgentSkills(options) {
|
|
11
|
+
const apiUrl = normalizeApiUrl(readOption(options.apiUrl) ?? defaultApiUrl());
|
|
12
|
+
const indexUrl = skillIndexUrl(apiUrl);
|
|
13
|
+
const index = await inspectSkillIndex(indexUrl);
|
|
14
|
+
const installed = detectInstalledAgentSkills();
|
|
15
|
+
return {
|
|
16
|
+
index_url: indexUrl,
|
|
17
|
+
available: {
|
|
18
|
+
detected: index.reachable,
|
|
19
|
+
names: index.skill_names,
|
|
20
|
+
skills: index.skills,
|
|
21
|
+
...(index.error ? { error: index.error } : {}),
|
|
22
|
+
},
|
|
23
|
+
installed,
|
|
24
|
+
recommended_install_command: recommendedSkillsInstallCommand(apiUrl),
|
|
25
|
+
install_command_args: recommendedSkillsInstallArgs(apiUrl),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export async function doctorAgentSkills(options) {
|
|
29
|
+
const apiUrl = normalizeApiUrl(readOption(options.apiUrl) ?? defaultApiUrl());
|
|
30
|
+
const indexUrl = skillIndexUrl(apiUrl);
|
|
31
|
+
const index = await inspectSkillIndex(indexUrl);
|
|
32
|
+
const installer = inspectSkillsInstaller();
|
|
33
|
+
const installed = detectInstalledAgentSkills();
|
|
34
|
+
const data = {
|
|
35
|
+
index_url: indexUrl,
|
|
36
|
+
index,
|
|
37
|
+
installer,
|
|
38
|
+
installed,
|
|
39
|
+
recommended_install_command: recommendedSkillsInstallCommand(apiUrl),
|
|
40
|
+
install_command_args: recommendedSkillsInstallArgs(apiUrl),
|
|
41
|
+
};
|
|
42
|
+
if (!index.reachable) {
|
|
43
|
+
throw new OxygenError("skills_index_unreachable", "Oxygen skill index is not reachable.", {
|
|
44
|
+
details: data,
|
|
45
|
+
exitCode: 1,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (!installer.available) {
|
|
49
|
+
throw new OxygenError("skills_installer_unavailable", "The local npx installer command is not available.", {
|
|
50
|
+
details: data,
|
|
51
|
+
exitCode: 1,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return data;
|
|
55
|
+
}
|
|
56
|
+
export function installAgentSkills(// skipcq: JS-R1005
|
|
57
|
+
options, runtime = {}) {
|
|
58
|
+
const apiUrl = normalizeApiUrl(readOption(options.apiUrl) ?? defaultApiUrl());
|
|
59
|
+
const indexUrl = skillIndexUrl(apiUrl);
|
|
60
|
+
const agents = readWords(options.agents ?? DEFAULT_SKILL_AGENTS);
|
|
61
|
+
const skill = readOption(options.skill) ?? "*";
|
|
62
|
+
const env = runtime.env ?? process.env;
|
|
63
|
+
const platform = runtime.platform ?? process.platform;
|
|
64
|
+
const args = installerAddArgs(indexUrl, agents, skill);
|
|
65
|
+
if (!options.project)
|
|
66
|
+
args.push("--global");
|
|
67
|
+
if (options.copy)
|
|
68
|
+
args.push("--copy");
|
|
69
|
+
const invocation = resolveCmdShimInvocation("npx", args, platform, env);
|
|
70
|
+
const spawn = runtime.spawnSync ?? wrapExecFileSync(runtime.execFileSync) ?? spawnSync;
|
|
71
|
+
const result = spawn(invocation.executable, invocation.args, {
|
|
72
|
+
encoding: "utf8",
|
|
73
|
+
env,
|
|
74
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
75
|
+
windowsHide: true,
|
|
76
|
+
...(platform === "win32" ? { windowsVerbatimArguments: true } : {}),
|
|
77
|
+
});
|
|
78
|
+
const stdout = typeof result.stdout === "string" ? result.stdout : "";
|
|
79
|
+
const stderr = typeof result.stderr === "string" ? result.stderr : "";
|
|
80
|
+
if (result.error || (typeof result.status === "number" && result.status !== 0)) {
|
|
81
|
+
const message = result.error instanceof Error ? result.error.message : null;
|
|
82
|
+
throw new OxygenError("skills_install_failed", "Unable to install Oxygen agent skills.", {
|
|
83
|
+
details: {
|
|
84
|
+
index_url: indexUrl,
|
|
85
|
+
executable: invocation.label,
|
|
86
|
+
exit_code: typeof result.status === "number" ? result.status : null,
|
|
87
|
+
stdout: stdout.slice(0, 2000),
|
|
88
|
+
stderr: (stderr || message || "unknown").slice(0, 2000),
|
|
89
|
+
},
|
|
90
|
+
exitCode: 1,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
indexUrl,
|
|
95
|
+
index_url: indexUrl,
|
|
96
|
+
agents,
|
|
97
|
+
skill,
|
|
98
|
+
scope: options.project ? "project" : "global",
|
|
99
|
+
output: stdout,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
// Adapt the legacy `execFileSync` runtime hook into a spawnSync-shaped return so
|
|
103
|
+
// existing test fixtures (update.test.ts) keep working without a coordinated
|
|
104
|
+
// rewrite. New tests should inject `spawnSync` directly.
|
|
105
|
+
function wrapExecFileSync(exec) {
|
|
106
|
+
if (!exec)
|
|
107
|
+
return undefined;
|
|
108
|
+
return ((command, args, opts) => {
|
|
109
|
+
try {
|
|
110
|
+
const stdout = exec(command, args, opts);
|
|
111
|
+
return {
|
|
112
|
+
status: 0,
|
|
113
|
+
signal: null,
|
|
114
|
+
output: [],
|
|
115
|
+
pid: 0,
|
|
116
|
+
stdout: typeof stdout === "string" ? stdout : Buffer.isBuffer(stdout) ? stdout.toString("utf8") : "",
|
|
117
|
+
stderr: "",
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
const failure = error;
|
|
122
|
+
return {
|
|
123
|
+
status: typeof failure.status === "number" ? failure.status : 1,
|
|
124
|
+
signal: null,
|
|
125
|
+
output: [],
|
|
126
|
+
pid: 0,
|
|
127
|
+
stdout: failure.stdout ?? "",
|
|
128
|
+
stderr: failure.stderr ?? failure.message ?? "",
|
|
129
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
export function runAutomaticSkillsInstall(options = {}) {
|
|
135
|
+
const apiUrl = normalizeApiUrl(readOption(options.apiUrl) ?? defaultApiUrl());
|
|
136
|
+
const env = options.env ?? process.env;
|
|
137
|
+
const platform = options.platform ?? process.platform;
|
|
138
|
+
if (env.OXYGEN_SKIP_SKILLS === "1") {
|
|
139
|
+
return skippedAutomaticSkillsInstall("OXYGEN_SKIP_SKILLS=1", apiUrl);
|
|
140
|
+
}
|
|
141
|
+
const command = recommendedSkillsInstallCommand(apiUrl);
|
|
142
|
+
try {
|
|
143
|
+
installAgentSkills({
|
|
144
|
+
apiUrl,
|
|
145
|
+
agents: DEFAULT_SKILL_AGENTS,
|
|
146
|
+
skill: "*",
|
|
147
|
+
json: true,
|
|
148
|
+
// `npx skills add` symlinks by default; on Windows that requires Developer
|
|
149
|
+
// Mode or admin elevation, so copy is the only reliable unattended path.
|
|
150
|
+
copy: platform === "win32",
|
|
151
|
+
}, {
|
|
152
|
+
env,
|
|
153
|
+
platform,
|
|
154
|
+
...(options.execFileSync ? { execFileSync: options.execFileSync } : {}),
|
|
155
|
+
...(options.spawnSync ? { spawnSync: options.spawnSync } : {}),
|
|
156
|
+
});
|
|
157
|
+
return {
|
|
158
|
+
attempted: true,
|
|
159
|
+
ok: true,
|
|
160
|
+
command,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
const failure = toFailure("skills install", error);
|
|
165
|
+
return {
|
|
166
|
+
attempted: true,
|
|
167
|
+
ok: false,
|
|
168
|
+
command,
|
|
169
|
+
error: failure.error,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
export function skippedAutomaticSkillsInstall(reason, apiUrl = normalizeApiUrl(defaultApiUrl())) {
|
|
174
|
+
return {
|
|
175
|
+
attempted: false,
|
|
176
|
+
ok: true,
|
|
177
|
+
command: recommendedSkillsInstallCommand(apiUrl),
|
|
178
|
+
reason,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
async function inspectSkillIndex(indexUrl) {
|
|
182
|
+
const controller = new AbortController();
|
|
183
|
+
const timeout = setTimeout(() => controller.abort(), 10_000);
|
|
184
|
+
timeout.unref?.();
|
|
185
|
+
try {
|
|
186
|
+
const response = await fetch(indexUrl, {
|
|
187
|
+
headers: { Accept: "application/json" },
|
|
188
|
+
signal: controller.signal,
|
|
189
|
+
});
|
|
190
|
+
const text = await response.text();
|
|
191
|
+
if (!response.ok) {
|
|
192
|
+
return emptySkillIndexInspection({
|
|
193
|
+
status: response.status,
|
|
194
|
+
error: { code: "http_error", message: `HTTP ${response.status}` },
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
let parsed;
|
|
198
|
+
try {
|
|
199
|
+
parsed = JSON.parse(text);
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
return emptySkillIndexInspection({
|
|
203
|
+
status: response.status,
|
|
204
|
+
error: {
|
|
205
|
+
code: "invalid_json",
|
|
206
|
+
message: error instanceof Error ? error.message : "Invalid JSON.",
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
let index;
|
|
211
|
+
try {
|
|
212
|
+
index = parseSkillIndex(parsed);
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
return emptySkillIndexInspection({
|
|
216
|
+
status: response.status,
|
|
217
|
+
error: {
|
|
218
|
+
code: error instanceof OxygenError ? error.code : "invalid_skill_index",
|
|
219
|
+
message: error instanceof Error ? error.message : "Skill index shape is invalid.",
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
reachable: true,
|
|
225
|
+
status: response.status,
|
|
226
|
+
version: index.version,
|
|
227
|
+
base_url: index.base_url,
|
|
228
|
+
skill_names: index.skills.map((skill) => skill.name),
|
|
229
|
+
skills: index.skills,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
const aborted = error instanceof Error && error.name === "AbortError";
|
|
234
|
+
return emptySkillIndexInspection({
|
|
235
|
+
status: null,
|
|
236
|
+
error: {
|
|
237
|
+
code: aborted ? "network_timeout" : "network_error",
|
|
238
|
+
message: error instanceof Error ? error.message : "Unable to reach skill index.",
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
finally {
|
|
243
|
+
clearTimeout(timeout);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function emptySkillIndexInspection(input) {
|
|
247
|
+
return {
|
|
248
|
+
reachable: false,
|
|
249
|
+
status: input.status,
|
|
250
|
+
version: null,
|
|
251
|
+
base_url: null,
|
|
252
|
+
skill_names: [],
|
|
253
|
+
skills: [],
|
|
254
|
+
error: input.error,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function parseSkillIndex(value) {
|
|
258
|
+
if (!isRecord(value) || !Array.isArray(value.skills)) {
|
|
259
|
+
throw new OxygenError("invalid_skill_index", "Skill index must include a skills array.", {
|
|
260
|
+
exitCode: 1,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
const skills = value.skills
|
|
264
|
+
.map((entry) => {
|
|
265
|
+
if (!isRecord(entry) || typeof entry.name !== "string" || !entry.name.trim())
|
|
266
|
+
return null;
|
|
267
|
+
return {
|
|
268
|
+
name: entry.name.trim(),
|
|
269
|
+
title: typeof entry.title === "string" ? entry.title : null,
|
|
270
|
+
description: typeof entry.description === "string" ? entry.description : null,
|
|
271
|
+
entrypoint: typeof entry.entrypoint === "string" ? entry.entrypoint : null,
|
|
272
|
+
};
|
|
273
|
+
})
|
|
274
|
+
.filter((entry) => entry !== null);
|
|
275
|
+
return {
|
|
276
|
+
version: typeof value.version === "string" ? value.version : null,
|
|
277
|
+
base_url: typeof value.base_url === "string" ? value.base_url : null,
|
|
278
|
+
skills,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
function inspectSkillsInstaller() {
|
|
282
|
+
const invocation = resolveCmdShimInvocation("npx", ["--version"], process.platform, process.env);
|
|
283
|
+
const result = spawnSync(invocation.executable, invocation.args, {
|
|
284
|
+
encoding: "utf8",
|
|
285
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
286
|
+
timeout: 5000,
|
|
287
|
+
windowsHide: true,
|
|
288
|
+
...(process.platform === "win32" ? { windowsVerbatimArguments: true } : {}),
|
|
289
|
+
});
|
|
290
|
+
const error = result.error instanceof Error ? result.error.message : null;
|
|
291
|
+
const stdout = typeof result.stdout === "string" ? result.stdout.trim() : "";
|
|
292
|
+
const stderr = typeof result.stderr === "string" ? result.stderr.trim() : "";
|
|
293
|
+
return {
|
|
294
|
+
command_path: "npx",
|
|
295
|
+
check_args: ["--version"],
|
|
296
|
+
invocation: { executable: invocation.label, args: invocation.args },
|
|
297
|
+
available: result.status === 0 && !error,
|
|
298
|
+
version: stdout || null,
|
|
299
|
+
...(error ? { error } : {}),
|
|
300
|
+
...(stderr ? { stderr: stderr.slice(0, 1000) } : {}),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function detectInstalledAgentSkills() {
|
|
304
|
+
const roots = [
|
|
305
|
+
{ agent: "codex", path: join(process.env.CODEX_HOME ?? join(homedir(), ".codex"), "skills") },
|
|
306
|
+
{ agent: "claude-code", path: join(process.env.CLAUDE_HOME ?? join(homedir(), ".claude"), "skills") },
|
|
307
|
+
{ agent: "cursor", path: join(process.env.CURSOR_HOME ?? join(homedir(), ".cursor"), "skills") },
|
|
308
|
+
{ agent: "project", path: resolve(".agents", "skills") },
|
|
309
|
+
];
|
|
310
|
+
const names = new Set();
|
|
311
|
+
const locations = [];
|
|
312
|
+
for (const root of roots) {
|
|
313
|
+
if (!existsSync(root.path))
|
|
314
|
+
continue;
|
|
315
|
+
try {
|
|
316
|
+
const rootNames = readdirSync(root.path, { withFileTypes: true })
|
|
317
|
+
.filter((entry) => entry.isDirectory() && entry.name.startsWith("oxygen-"))
|
|
318
|
+
.map((entry) => entry.name)
|
|
319
|
+
.sort();
|
|
320
|
+
for (const name of rootNames)
|
|
321
|
+
names.add(name);
|
|
322
|
+
locations.push({ ...root, names: rootNames });
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
locations.push({ ...root, names: [] });
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
detected: locations.length > 0,
|
|
330
|
+
names: [...names].sort(),
|
|
331
|
+
locations,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
function skillIndexUrl(apiUrl) {
|
|
335
|
+
return `${apiUrl}${SKILL_INDEX_PATH}`;
|
|
336
|
+
}
|
|
337
|
+
function recommendedSkillsInstallCommand(apiUrl) {
|
|
338
|
+
return recommendedSkillsInstallArgs(apiUrl)
|
|
339
|
+
.map((entry) => entry === "*" ? "'*'" : entry)
|
|
340
|
+
.join(" ");
|
|
341
|
+
}
|
|
342
|
+
function recommendedSkillsInstallArgs(apiUrl) {
|
|
343
|
+
const args = ["oxygen", "skills", "install"];
|
|
344
|
+
if (apiUrl !== normalizeApiUrl(defaultApiUrl())) {
|
|
345
|
+
args.push("--api-url", apiUrl);
|
|
346
|
+
}
|
|
347
|
+
args.push("--agents", ...DEFAULT_SKILL_AGENTS, "--skill", "*", "--json");
|
|
348
|
+
return args;
|
|
349
|
+
}
|
|
350
|
+
function installerAddArgs(indexUrl, agents, skill) {
|
|
351
|
+
return [
|
|
352
|
+
"skills",
|
|
353
|
+
"add",
|
|
354
|
+
indexUrl,
|
|
355
|
+
"--agents",
|
|
356
|
+
...agents,
|
|
357
|
+
"--yes",
|
|
358
|
+
"--skill",
|
|
359
|
+
skill,
|
|
360
|
+
"--full-depth",
|
|
361
|
+
];
|
|
362
|
+
}
|
|
363
|
+
function readWords(value) {
|
|
364
|
+
const text = Array.isArray(value) ? value.join(" ") : value;
|
|
365
|
+
return text
|
|
366
|
+
.split(/[,\s]+/)
|
|
367
|
+
.map((entry) => entry.trim())
|
|
368
|
+
.filter(Boolean);
|
|
369
|
+
}
|
|
370
|
+
function readOption(value) {
|
|
371
|
+
return value?.trim() ? value.trim() : null;
|
|
372
|
+
}
|
|
373
|
+
function isRecord(value) {
|
|
374
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
375
|
+
}
|
package/dist/update.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { type AutomaticSkillsInstallResult, type ExecFileSyncLike } from "./skills.js";
|
|
2
3
|
type SpawnResult = ReturnType<typeof spawnSync>;
|
|
3
4
|
type SpawnSyncLike = (command: string, args: string[], options: {
|
|
4
5
|
encoding: BufferEncoding;
|
|
@@ -18,12 +19,14 @@ export type UpdateResult = {
|
|
|
18
19
|
command: string;
|
|
19
20
|
dry_run: boolean;
|
|
20
21
|
updated: boolean;
|
|
22
|
+
skills_install: AutomaticSkillsInstallResult;
|
|
21
23
|
};
|
|
22
24
|
export declare function updateCli(options: UpdateOptions, runtime?: {
|
|
23
25
|
env?: NodeJS.ProcessEnv;
|
|
24
26
|
platform?: NodeJS.Platform;
|
|
25
27
|
modulePath?: string;
|
|
26
28
|
spawnSync?: SpawnSyncLike;
|
|
29
|
+
execFileSync?: ExecFileSyncLike;
|
|
27
30
|
}): UpdateResult;
|
|
28
31
|
export declare function detectCliInstallPrefix(modulePath?: string): string | null;
|
|
29
32
|
export declare function detectCliInstallPrefixFromPath(modulePath: string): string | null;
|
package/dist/update.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import { OXYGEN_VERSION, OxygenError } from "@oxygen/shared";
|
|
4
|
+
import { runAutomaticSkillsInstall, skippedAutomaticSkillsInstall, } from "./skills.js";
|
|
5
|
+
import { quoteWindowsCmdArg } from "./windows-shim.js";
|
|
4
6
|
const DEFAULT_CLI_PACKAGE_SPEC = "@oxygen-agent/cli@latest";
|
|
7
|
+
// skipcq: JS-R1005 — handles platform/install-mode/dry-run combinations end-to-end
|
|
5
8
|
export function updateCli(options, runtime = {}) {
|
|
6
9
|
const env = runtime.env ?? process.env;
|
|
7
10
|
const platform = runtime.platform ?? process.platform;
|
|
@@ -22,6 +25,7 @@ export function updateCli(options, runtime = {}) {
|
|
|
22
25
|
command,
|
|
23
26
|
dry_run: true,
|
|
24
27
|
updated: false,
|
|
28
|
+
skills_install: skippedAutomaticSkillsInstall("dry_run"),
|
|
25
29
|
};
|
|
26
30
|
}
|
|
27
31
|
const attemptedExecutables = [];
|
|
@@ -48,6 +52,11 @@ export function updateCli(options, runtime = {}) {
|
|
|
48
52
|
command,
|
|
49
53
|
dry_run: false,
|
|
50
54
|
updated: true,
|
|
55
|
+
skills_install: runAutomaticSkillsInstall({
|
|
56
|
+
env,
|
|
57
|
+
platform,
|
|
58
|
+
...(runtime.execFileSync ? { execFileSync: runtime.execFileSync } : {}),
|
|
59
|
+
}),
|
|
51
60
|
};
|
|
52
61
|
}
|
|
53
62
|
break;
|
|
@@ -104,11 +113,6 @@ function resolveNpmInvocations(args, platform, env) {
|
|
|
104
113
|
label: shell,
|
|
105
114
|
}];
|
|
106
115
|
}
|
|
107
|
-
function quoteWindowsCmdArg(value) {
|
|
108
|
-
if (/^[A-Za-z0-9_@+=:,./\\-]+$/.test(value))
|
|
109
|
-
return value;
|
|
110
|
-
return `"${value.replace(/(["^&|<>()%])/g, "^$1")}"`;
|
|
111
|
-
}
|
|
112
116
|
function quoteForDisplay(value) {
|
|
113
117
|
if (/^[A-Za-z0-9_@+=:,./-]+$/.test(value))
|
|
114
118
|
return value;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type CmdShimInvocation = {
|
|
2
|
+
executable: string;
|
|
3
|
+
args: string[];
|
|
4
|
+
label: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function resolveCmdShimInvocation(command: string, args: string[], platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): CmdShimInvocation;
|
|
7
|
+
export declare function quoteWindowsCmdArg(value: string): string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Windows-aware command launcher. Node's `execFileSync`/`spawnSync` cannot
|
|
2
|
+
// resolve `.cmd`/`.bat` shims (like `npx.cmd`) from a bare command name, so on
|
|
3
|
+
// Windows we route through `cmd.exe /d /s /c <command> <args>` with
|
|
4
|
+
// `windowsVerbatimArguments: true`. Spawn options must enable that flag at the
|
|
5
|
+
// caller — see `update.ts` and `skills.ts` for the pattern.
|
|
6
|
+
export function resolveCmdShimInvocation(command, args, platform = process.platform, env = process.env) {
|
|
7
|
+
if (platform !== "win32") {
|
|
8
|
+
return { executable: command, args, label: command };
|
|
9
|
+
}
|
|
10
|
+
const shell = env.ComSpec?.trim() || "cmd.exe";
|
|
11
|
+
return {
|
|
12
|
+
executable: shell,
|
|
13
|
+
args: ["/d", "/s", "/c", [command, ...args].map(quoteWindowsCmdArg).join(" ")],
|
|
14
|
+
label: shell,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function quoteWindowsCmdArg(value) {
|
|
18
|
+
if (/^[A-Za-z0-9_@+=:,./\\-]+$/.test(value))
|
|
19
|
+
return value;
|
|
20
|
+
return `"${value.replace(/(["^&|<>()%])/g, "^$1")}"`;
|
|
21
|
+
}
|
|
@@ -52,6 +52,7 @@ const IDENTIFIER_COLUMN_RE = /(?:^|[^a-z0-9])(phone|mobile|tel(?:ephone)?|fax|zi
|
|
|
52
52
|
* a value as the absence of a string. Surface-specific concerns (HTML
|
|
53
53
|
* escaping, ellipsis budgets) belong outside this function.
|
|
54
54
|
*/
|
|
55
|
+
// skipcq: JS-R1005 — dispatches across cell types + surfaces; branches are intrinsic
|
|
55
56
|
export function formatCellForDisplay(value, column, options) {
|
|
56
57
|
if (value === null || value === undefined || value === "")
|
|
57
58
|
return "";
|
|
@@ -198,9 +199,7 @@ function shouldRescueColumn(column) {
|
|
|
198
199
|
const dataType = readDataType(column);
|
|
199
200
|
if (dataType !== "text" && dataType !== "numeric")
|
|
200
201
|
return false;
|
|
201
|
-
|
|
202
|
-
return false;
|
|
203
|
-
return true;
|
|
202
|
+
return !columnLooksLikeIdentifier(column);
|
|
204
203
|
}
|
|
205
204
|
/**
|
|
206
205
|
* Decide whether the same value-level + column-level guards used inside
|
|
@@ -10,11 +10,16 @@ export type NewTableImportRows = {
|
|
|
10
10
|
rows: Record<string, unknown>[];
|
|
11
11
|
keyBySource: Record<string, string>;
|
|
12
12
|
};
|
|
13
|
+
export declare const MAX_BUFFERED_IMPORT_PARSE_BYTES: number;
|
|
13
14
|
export declare function inferRowsFileFormat(path: string): RowsFileFormat;
|
|
14
15
|
export declare function normalizeRowsFormat(value: string | undefined, fallback: RowsFileFormat): RowsFileFormat;
|
|
15
16
|
export declare function parseRowsFileBuffer(buffer: Buffer, format: RowsFileFormat, options?: {
|
|
16
17
|
sheet?: string;
|
|
17
18
|
}): Promise<Record<string, unknown>[]>;
|
|
19
|
+
export declare function iterateRowsFileBufferBatches(buffer: Buffer, format: RowsFileFormat, options?: {
|
|
20
|
+
sheet?: string;
|
|
21
|
+
batchSize?: number;
|
|
22
|
+
}): AsyncGenerator<Record<string, unknown>[]>;
|
|
18
23
|
export declare function parseRowsText(text: string, format: Exclude<RowsFileFormat, "xlsx">): Record<string, unknown>[];
|
|
19
24
|
export declare function inferImportColumnLabels(rows: Record<string, unknown>[]): string[];
|
|
20
25
|
export declare function normalizeRowsForNewTable(rows: Record<string, unknown>[]): NewTableImportRows;
|