agenticros 0.3.5 → 0.4.1
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/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +34 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/skills.d.ts +3 -5
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/commands/skills.js +21 -7
- package/dist/commands/skills.js.map +1 -1
- package/dist/util/skills.d.ts +6 -0
- package/dist/util/skills.d.ts.map +1 -1
- package/dist/util/skills.js +69 -1
- package/dist/util/skills.js.map +1 -1
- package/package.json +2 -1
- package/runtime/BUNDLE.json +1 -1
- package/runtime/packages/agenticros/src/index.ts +8 -1
- package/runtime/packages/agenticros/src/tools/ros2-capabilities.ts +9 -12
- package/runtime/packages/agenticros-claude-code/dist/config.d.ts +5 -3
- package/runtime/packages/agenticros-claude-code/dist/config.d.ts.map +1 -1
- package/runtime/packages/agenticros-claude-code/dist/config.js +32 -10
- package/runtime/packages/agenticros-claude-code/dist/config.js.map +1 -1
- package/runtime/packages/agenticros-claude-code/dist/index.js +3 -3
- package/runtime/packages/agenticros-claude-code/dist/index.js.map +1 -1
- package/runtime/packages/agenticros-claude-code/dist/tools.d.ts.map +1 -1
- package/runtime/packages/agenticros-claude-code/dist/tools.js +8 -8
- package/runtime/packages/agenticros-claude-code/dist/tools.js.map +1 -1
- package/runtime/packages/agenticros-claude-code/src/config.ts +33 -10
- package/runtime/packages/agenticros-claude-code/src/index.ts +3 -3
- package/runtime/packages/agenticros-claude-code/src/tools.ts +11 -8
- package/runtime/packages/agenticros-gemini/src/config.ts +27 -18
- package/runtime/packages/agenticros-gemini/src/index.ts +2 -2
- package/runtime/packages/agenticros-gemini/src/tools.ts +5 -2
- package/runtime/packages/core/package.json +1 -1
- package/runtime/packages/core/src/__tests__/skill-refs.test.ts +77 -0
- package/runtime/packages/core/src/config.ts +8 -0
- package/runtime/packages/core/src/discoverable-capabilities.ts +178 -0
- package/runtime/packages/core/src/external-capability.ts +16 -3
- package/runtime/packages/core/src/index.ts +30 -0
- package/runtime/packages/core/src/skill-refs.ts +330 -0
- package/runtime/pnpm-lock.yaml +24 -0
- package/runtime/scripts/check-cli-tarball-install.mjs +41 -8
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* skillRefs → ~/.agenticros/skills-cache resolver (marketplace auto-fetch v1).
|
|
3
|
+
*
|
|
4
|
+
* Declarative refs like `agenticros/navigate-to` or `owner/skill@main` are
|
|
5
|
+
* resolved via the skills marketplace install API, cloned into the cache,
|
|
6
|
+
* built if needed, and returned as absolute skillPaths for capability
|
|
7
|
+
* reading / OpenClaw loading.
|
|
8
|
+
*
|
|
9
|
+
* Never auto-upgrades: if the cache dir for a pin already exists, reuse it
|
|
10
|
+
* (optional pull when AGENTICROS_SKILLS_CACHE_PULL=1).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
import { basename, join } from "node:path";
|
|
16
|
+
import { execFileSync } from "node:child_process";
|
|
17
|
+
import type { AgenticROSConfig } from "./config.js";
|
|
18
|
+
|
|
19
|
+
export const DEFAULT_SKILLS_API = "https://skills.agenticros.com/api";
|
|
20
|
+
export const DEFAULT_SKILLS_CACHE_DIR = join(homedir(), ".agenticros", "skills-cache");
|
|
21
|
+
|
|
22
|
+
export interface ParsedSkillRef {
|
|
23
|
+
/** owner/skill (lowercase) */
|
|
24
|
+
marketplaceRef: string;
|
|
25
|
+
owner: string;
|
|
26
|
+
skill: string;
|
|
27
|
+
/** git ref / branch pin; default main */
|
|
28
|
+
gitRef: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface InstallDescriptor {
|
|
32
|
+
slug: string;
|
|
33
|
+
marketplaceRef?: string;
|
|
34
|
+
skillId: string;
|
|
35
|
+
packageName: string;
|
|
36
|
+
githubUrl: string;
|
|
37
|
+
ref: string;
|
|
38
|
+
buildCmd: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ResolveSkillRefsOptions {
|
|
42
|
+
apiBase?: string;
|
|
43
|
+
cacheDir?: string;
|
|
44
|
+
/** When true, git pull --ff-only if cache already exists. */
|
|
45
|
+
pullIfPresent?: boolean;
|
|
46
|
+
/** Skip network / clone (return only paths that already exist in cache). */
|
|
47
|
+
offline?: boolean;
|
|
48
|
+
onLog?: (msg: string) => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ResolveSkillRefsResult {
|
|
52
|
+
/** Absolute directories ready to merge into skillPaths. */
|
|
53
|
+
paths: string[];
|
|
54
|
+
errors: Array<{ ref: string; error: string }>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Parse `owner/skill` or `owner/skill@branch`. */
|
|
58
|
+
export function parseSkillRef(raw: string): ParsedSkillRef | null {
|
|
59
|
+
const trimmed = raw.trim();
|
|
60
|
+
if (!trimmed) return null;
|
|
61
|
+
const at = trimmed.lastIndexOf("@");
|
|
62
|
+
let body = trimmed;
|
|
63
|
+
let gitRef = "main";
|
|
64
|
+
if (at > 0) {
|
|
65
|
+
body = trimmed.slice(0, at);
|
|
66
|
+
gitRef = trimmed.slice(at + 1).trim() || "main";
|
|
67
|
+
}
|
|
68
|
+
const parts = body.split("/").filter(Boolean);
|
|
69
|
+
if (parts.length < 2) return null;
|
|
70
|
+
const owner = parts[0]!.toLowerCase();
|
|
71
|
+
const skill = parts.slice(1).join("/").toLowerCase();
|
|
72
|
+
if (!owner || !skill) return null;
|
|
73
|
+
return {
|
|
74
|
+
marketplaceRef: `${owner}/${skill}`,
|
|
75
|
+
owner,
|
|
76
|
+
skill,
|
|
77
|
+
gitRef,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function skillsApiBase(override?: string): string {
|
|
82
|
+
return (override || process.env.AGENTICROS_SKILLS_API || DEFAULT_SKILLS_API).replace(
|
|
83
|
+
/\/+$/,
|
|
84
|
+
"",
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function skillsCacheDir(override?: string): string {
|
|
89
|
+
return override || process.env.AGENTICROS_SKILLS_CACHE || DEFAULT_SKILLS_CACHE_DIR;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function skillApiPath(apiBase: string, ref: string): string {
|
|
93
|
+
const [owner, ...rest] = ref.split("/");
|
|
94
|
+
const skill = rest.join("/");
|
|
95
|
+
return `${apiBase}/skills/${encodeURIComponent(owner!)}/${encodeURIComponent(skill)}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function fetchInstallDescriptor(
|
|
99
|
+
marketplaceRef: string,
|
|
100
|
+
apiBase?: string,
|
|
101
|
+
): Promise<InstallDescriptor> {
|
|
102
|
+
const base = skillsApiBase(apiBase);
|
|
103
|
+
const url = `${skillApiPath(base, marketplaceRef)}/install`;
|
|
104
|
+
const res = await fetch(url, { headers: { Accept: "application/json" } });
|
|
105
|
+
if (!res.ok) {
|
|
106
|
+
const body = await res.text().catch(() => "");
|
|
107
|
+
throw new Error(`Marketplace install ${res.status}: ${body.slice(0, 200)}`);
|
|
108
|
+
}
|
|
109
|
+
const desc = (await res.json()) as InstallDescriptor;
|
|
110
|
+
if (!desc.githubUrl) {
|
|
111
|
+
throw new Error(`Install descriptor for ${marketplaceRef} has no githubUrl`);
|
|
112
|
+
}
|
|
113
|
+
return desc;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function cachePathFor(
|
|
117
|
+
cacheDir: string,
|
|
118
|
+
owner: string,
|
|
119
|
+
skill: string,
|
|
120
|
+
gitRef: string,
|
|
121
|
+
): string {
|
|
122
|
+
const safeRef = gitRef.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
123
|
+
return join(cacheDir, owner, skill, safeRef);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function hasBuiltEntry(dir: string): boolean {
|
|
127
|
+
try {
|
|
128
|
+
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")) as {
|
|
129
|
+
main?: string;
|
|
130
|
+
};
|
|
131
|
+
const main = pkg.main ?? "dist/index.js";
|
|
132
|
+
return existsSync(join(dir, main));
|
|
133
|
+
} catch {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function run(cmd: string, args: string[], cwd: string, log: (m: string) => void): void {
|
|
139
|
+
log(`$ ${cmd} ${args.join(" ")} (in ${cwd})`);
|
|
140
|
+
execFileSync(cmd, args, { cwd, stdio: "inherit" });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function hasBin(name: string): boolean {
|
|
144
|
+
try {
|
|
145
|
+
execFileSync(name, ["--version"], { stdio: "ignore" });
|
|
146
|
+
return true;
|
|
147
|
+
} catch {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Ensure one skillRef is present under the cache. Returns absolute path or throws.
|
|
154
|
+
*/
|
|
155
|
+
export async function ensureSkillRefCached(
|
|
156
|
+
rawRef: string,
|
|
157
|
+
opts: ResolveSkillRefsOptions = {},
|
|
158
|
+
): Promise<string> {
|
|
159
|
+
const parsed = parseSkillRef(rawRef);
|
|
160
|
+
if (!parsed) {
|
|
161
|
+
throw new Error(`Invalid skillRef "${rawRef}" (expected owner/skill or owner/skill@ref)`);
|
|
162
|
+
}
|
|
163
|
+
const log = opts.onLog ?? (() => undefined);
|
|
164
|
+
const cacheRoot = skillsCacheDir(opts.cacheDir);
|
|
165
|
+
const dir = cachePathFor(cacheRoot, parsed.owner, parsed.skill, parsed.gitRef);
|
|
166
|
+
|
|
167
|
+
if (existsSync(join(dir, "package.json"))) {
|
|
168
|
+
if (opts.pullIfPresent || process.env.AGENTICROS_SKILLS_CACHE_PULL === "1") {
|
|
169
|
+
try {
|
|
170
|
+
run("git", ["pull", "--ff-only"], dir, log);
|
|
171
|
+
} catch (e) {
|
|
172
|
+
log(`git pull failed (keeping cache): ${e instanceof Error ? e.message : String(e)}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (!hasBuiltEntry(dir) && !opts.offline) {
|
|
176
|
+
await buildInPlace(dir, log);
|
|
177
|
+
}
|
|
178
|
+
return dir;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (opts.offline) {
|
|
182
|
+
throw new Error(`skillRef ${parsed.marketplaceRef} not in cache (offline)`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const descriptor = await fetchInstallDescriptor(parsed.marketplaceRef, opts.apiBase);
|
|
186
|
+
const gitRef = parsed.gitRef !== "main" ? parsed.gitRef : descriptor.ref || "main";
|
|
187
|
+
const repoUrl = descriptor.githubUrl.endsWith(".git")
|
|
188
|
+
? descriptor.githubUrl
|
|
189
|
+
: `${descriptor.githubUrl}.git`;
|
|
190
|
+
|
|
191
|
+
mkdirSync(join(cacheRoot, parsed.owner, parsed.skill), { recursive: true });
|
|
192
|
+
if (!existsSync(join(dir, ".git"))) {
|
|
193
|
+
log(`Cloning ${repoUrl} (${gitRef}) → ${dir}`);
|
|
194
|
+
try {
|
|
195
|
+
run(
|
|
196
|
+
"git",
|
|
197
|
+
["clone", "--branch", gitRef, "--single-branch", "--depth", "1", repoUrl, dir],
|
|
198
|
+
join(cacheRoot, parsed.owner, parsed.skill),
|
|
199
|
+
log,
|
|
200
|
+
);
|
|
201
|
+
} catch {
|
|
202
|
+
// Branch pin may be a tag or default branch name mismatch — full clone + checkout.
|
|
203
|
+
run("git", ["clone", "--depth", "1", repoUrl, dir], join(cacheRoot, parsed.owner, parsed.skill), log);
|
|
204
|
+
try {
|
|
205
|
+
run("git", ["checkout", gitRef], dir, log);
|
|
206
|
+
} catch {
|
|
207
|
+
/* stay on default branch */
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Marker for which marketplace ref produced this cache entry
|
|
213
|
+
try {
|
|
214
|
+
writeFileSync(
|
|
215
|
+
join(dir, ".agenticros-skill-ref.json"),
|
|
216
|
+
JSON.stringify(
|
|
217
|
+
{
|
|
218
|
+
marketplaceRef: parsed.marketplaceRef,
|
|
219
|
+
gitRef,
|
|
220
|
+
githubUrl: descriptor.githubUrl,
|
|
221
|
+
cachedAt: new Date().toISOString(),
|
|
222
|
+
},
|
|
223
|
+
null,
|
|
224
|
+
2,
|
|
225
|
+
),
|
|
226
|
+
);
|
|
227
|
+
} catch {
|
|
228
|
+
/* ignore */
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
await buildInPlace(dir, log);
|
|
232
|
+
return dir;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function buildInPlace(dir: string, log: (m: string) => void): Promise<void> {
|
|
236
|
+
if (hasBuiltEntry(dir)) return;
|
|
237
|
+
const useNpm = !hasBin("pnpm");
|
|
238
|
+
if (useNpm) {
|
|
239
|
+
run("npm", ["install"], dir, log);
|
|
240
|
+
run("npm", ["run", "build"], dir, log);
|
|
241
|
+
} else {
|
|
242
|
+
run("pnpm", ["install"], dir, log);
|
|
243
|
+
run("pnpm", ["run", "build"], dir, log);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Resolve all config.skillRefs into absolute directories.
|
|
249
|
+
* Does not mutate config — caller merges into skillPaths.
|
|
250
|
+
*/
|
|
251
|
+
export async function resolveSkillRefs(
|
|
252
|
+
refs: string[],
|
|
253
|
+
opts: ResolveSkillRefsOptions = {},
|
|
254
|
+
): Promise<ResolveSkillRefsResult> {
|
|
255
|
+
const paths: string[] = [];
|
|
256
|
+
const errors: ResolveSkillRefsResult["errors"] = [];
|
|
257
|
+
const seen = new Set<string>();
|
|
258
|
+
for (const raw of refs) {
|
|
259
|
+
try {
|
|
260
|
+
const dir = await ensureSkillRefCached(raw, opts);
|
|
261
|
+
if (!seen.has(dir)) {
|
|
262
|
+
seen.add(dir);
|
|
263
|
+
paths.push(dir);
|
|
264
|
+
}
|
|
265
|
+
} catch (e) {
|
|
266
|
+
errors.push({
|
|
267
|
+
ref: raw,
|
|
268
|
+
error: e instanceof Error ? e.message : String(e),
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return { paths, errors };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Return a config copy with skillRefs resolved into skillPaths (deduped).
|
|
277
|
+
* Sync-friendly path: if offline or empty refs, returns config unchanged
|
|
278
|
+
* except merging any already-cached paths when possible without network.
|
|
279
|
+
*/
|
|
280
|
+
export async function withResolvedSkillRefs(
|
|
281
|
+
config: AgenticROSConfig,
|
|
282
|
+
opts: ResolveSkillRefsOptions = {},
|
|
283
|
+
): Promise<{ config: AgenticROSConfig; errors: ResolveSkillRefsResult["errors"] }> {
|
|
284
|
+
const refs = config.skillRefs ?? [];
|
|
285
|
+
if (refs.length === 0) {
|
|
286
|
+
return { config, errors: [] };
|
|
287
|
+
}
|
|
288
|
+
const { paths, errors } = await resolveSkillRefs(refs, opts);
|
|
289
|
+
const merged = [...(config.skillPaths ?? [])];
|
|
290
|
+
const have = new Set(merged.map((p) => p));
|
|
291
|
+
for (const p of paths) {
|
|
292
|
+
if (!have.has(p)) {
|
|
293
|
+
have.add(p);
|
|
294
|
+
merged.push(p);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return {
|
|
298
|
+
config: { ...config, skillPaths: merged },
|
|
299
|
+
errors,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Sync: merge skillRefs that are already present under the skills-cache
|
|
305
|
+
* into skillPaths. No network — use CLI install / ensureSkillRefCached to
|
|
306
|
+
* populate the cache first. Safe for OpenClaw's synchronous register().
|
|
307
|
+
*/
|
|
308
|
+
export function applyCachedSkillRefs(config: AgenticROSConfig): AgenticROSConfig {
|
|
309
|
+
const refs = config.skillRefs ?? [];
|
|
310
|
+
if (refs.length === 0) return config;
|
|
311
|
+
const cacheRoot = skillsCacheDir();
|
|
312
|
+
const merged = [...(config.skillPaths ?? [])];
|
|
313
|
+
const have = new Set(merged);
|
|
314
|
+
for (const raw of refs) {
|
|
315
|
+
const parsed = parseSkillRef(raw);
|
|
316
|
+
if (!parsed) continue;
|
|
317
|
+
const dir = cachePathFor(cacheRoot, parsed.owner, parsed.skill, parsed.gitRef);
|
|
318
|
+
if (existsSync(join(dir, "package.json")) && !have.has(dir)) {
|
|
319
|
+
have.add(dir);
|
|
320
|
+
merged.push(dir);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (merged.length === (config.skillPaths ?? []).length) return config;
|
|
324
|
+
return { ...config, skillPaths: merged };
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** Repo name hint from github URL (for logging). */
|
|
328
|
+
export function githubRepoBasename(githubUrl: string): string {
|
|
329
|
+
return basename(githubUrl.replace(/\.git$/, "").replace(/\/+$/, ""));
|
|
330
|
+
}
|
package/runtime/pnpm-lock.yaml
CHANGED
|
@@ -65,6 +65,9 @@ importers:
|
|
|
65
65
|
|
|
66
66
|
packages/agenticros-cli:
|
|
67
67
|
dependencies:
|
|
68
|
+
'@agenticros/core':
|
|
69
|
+
specifier: ^0.7.0
|
|
70
|
+
version: 0.7.0
|
|
68
71
|
'@inquirer/prompts':
|
|
69
72
|
specifier: ^7.0.0
|
|
70
73
|
version: 7.10.1(@types/node@20.19.35)
|
|
@@ -175,6 +178,10 @@ importers:
|
|
|
175
178
|
|
|
176
179
|
packages:
|
|
177
180
|
|
|
181
|
+
'@agenticros/core@0.7.0':
|
|
182
|
+
resolution: {integrity: sha512-tbcKSf34DVD+pO51goHw1zbu/L12nP3oPvOHoFJK48qaZ4kuejEepljo5xUNB84AtA/QnH9quMPUZbc8rVF8zQ==}
|
|
183
|
+
engines: {node: '>=20'}
|
|
184
|
+
|
|
178
185
|
'@eclipse-zenoh/zenoh-ts@1.9.0':
|
|
179
186
|
resolution: {integrity: sha512-qG3VR9Yt81+KjH0WOybyKcNVS0vtBmmhyUJ7dCc5n+8AeAEYTpEvMBTh43Lwu8P+imzMYvFqjaMAO2HFK+x9hA==}
|
|
180
187
|
|
|
@@ -1974,6 +1981,23 @@ packages:
|
|
|
1974
1981
|
|
|
1975
1982
|
snapshots:
|
|
1976
1983
|
|
|
1984
|
+
'@agenticros/core@0.7.0':
|
|
1985
|
+
dependencies:
|
|
1986
|
+
'@eclipse-zenoh/zenoh-ts': 1.9.0(patch_hash=pdjq6ms3zvqwikn6vpqzasydsy)
|
|
1987
|
+
'@foxglove/cdr': 3.5.0
|
|
1988
|
+
'@foxglove/rosmsg': 4.2.2
|
|
1989
|
+
'@foxglove/rosmsg2-serialization': 3.1.0
|
|
1990
|
+
ws: 8.19.0
|
|
1991
|
+
zod: 3.25.76
|
|
1992
|
+
optionalDependencies:
|
|
1993
|
+
node-datachannel: 0.12.0
|
|
1994
|
+
rclnodejs: 1.8.2
|
|
1995
|
+
transitivePeerDependencies:
|
|
1996
|
+
- bufferutil
|
|
1997
|
+
- jiti
|
|
1998
|
+
- supports-color
|
|
1999
|
+
- utf-8-validate
|
|
2000
|
+
|
|
1977
2001
|
'@eclipse-zenoh/zenoh-ts@1.9.0(patch_hash=pdjq6ms3zvqwikn6vpqzasydsy)':
|
|
1978
2002
|
dependencies:
|
|
1979
2003
|
'@thi.ng/leb128': 3.1.79
|
|
@@ -32,7 +32,16 @@
|
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
34
|
import { execSync, spawnSync } from "node:child_process";
|
|
35
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
existsSync,
|
|
37
|
+
mkdirSync,
|
|
38
|
+
mkdtempSync,
|
|
39
|
+
readdirSync,
|
|
40
|
+
readFileSync,
|
|
41
|
+
rmSync,
|
|
42
|
+
statSync,
|
|
43
|
+
writeFileSync,
|
|
44
|
+
} from "node:fs";
|
|
36
45
|
import { tmpdir } from "node:os";
|
|
37
46
|
import { dirname, join, resolve } from "node:path";
|
|
38
47
|
import { fileURLToPath } from "node:url";
|
|
@@ -132,6 +141,31 @@ try {
|
|
|
132
141
|
}
|
|
133
142
|
ok(`Extracted to ${pkgDir}`);
|
|
134
143
|
|
|
144
|
+
// 3a. published package.json must not contain pnpm workspace: protocol
|
|
145
|
+
// (npm / npx consumers cannot resolve workspace:* — broke agenticros@0.4.0).
|
|
146
|
+
step("Checking published package.json has no workspace: dependencies...");
|
|
147
|
+
const publishedPkg = JSON.parse(
|
|
148
|
+
readFileSync(join(pkgDir, "package.json"), "utf8"),
|
|
149
|
+
);
|
|
150
|
+
const badDeps = [];
|
|
151
|
+
for (const section of ["dependencies", "optionalDependencies", "peerDependencies"]) {
|
|
152
|
+
const block = publishedPkg[section];
|
|
153
|
+
if (!block || typeof block !== "object") continue;
|
|
154
|
+
for (const [name, range] of Object.entries(block)) {
|
|
155
|
+
if (typeof range === "string" && range.startsWith("workspace:")) {
|
|
156
|
+
badDeps.push(`${section}.${name}=${range}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (badDeps.length > 0) {
|
|
161
|
+
fail(
|
|
162
|
+
`Published package.json still has workspace: protocol (npm cannot install this):\n ${badDeps.join("\n ")}\n` +
|
|
163
|
+
`Use a semver range against the published @agenticros/core (e.g. "^0.7.0").`,
|
|
164
|
+
);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
ok("No workspace: protocol in published dependencies.");
|
|
168
|
+
|
|
135
169
|
// 3. critical files present in the SHIPPED tarball
|
|
136
170
|
step("Verifying critical runtime files are in the tarball...");
|
|
137
171
|
const runtimeInTar = join(pkgDir, "runtime");
|
|
@@ -150,8 +184,7 @@ try {
|
|
|
150
184
|
|
|
151
185
|
// 5. write inline .npmrc (mirrors init.ts writeInitNpmrcInline)
|
|
152
186
|
step("Writing inline .npmrc (mirroring writeInitNpmrcInline)...");
|
|
153
|
-
|
|
154
|
-
fs.writeFileSync(
|
|
187
|
+
writeFileSync(
|
|
155
188
|
join(installDir, ".npmrc"),
|
|
156
189
|
[
|
|
157
190
|
"shamefully-hoist=false",
|
|
@@ -220,9 +253,9 @@ try {
|
|
|
220
253
|
step("Simulating refresh over a pre-existing snapshot (regression test for 0.1.7)...");
|
|
221
254
|
const fakeOld = join(work, "old-install");
|
|
222
255
|
mkdirSync(fakeOld);
|
|
223
|
-
|
|
256
|
+
writeFileSync(join(fakeOld, "tsconfig.base.json"), '{"old":"to-be-overwritten"}');
|
|
224
257
|
mkdirSync(join(fakeOld, "packages"), { recursive: true });
|
|
225
|
-
|
|
258
|
+
writeFileSync(
|
|
226
259
|
join(fakeOld, "packages", "preserved.txt"),
|
|
227
260
|
"must survive overlay refresh",
|
|
228
261
|
);
|
|
@@ -234,7 +267,7 @@ try {
|
|
|
234
267
|
const src = join(runtimeInTar, rel);
|
|
235
268
|
const dst = join(fakeOld, rel);
|
|
236
269
|
if (!existsSync(src)) continue;
|
|
237
|
-
const srcIsDir =
|
|
270
|
+
const srcIsDir = statSync(src).isDirectory();
|
|
238
271
|
if (srcIsDir && existsSync(dst)) {
|
|
239
272
|
run("cp", ["-a", `${src}/.`, `${dst}/`]);
|
|
240
273
|
} else {
|
|
@@ -243,12 +276,12 @@ try {
|
|
|
243
276
|
}
|
|
244
277
|
}
|
|
245
278
|
|
|
246
|
-
const tsStat =
|
|
279
|
+
const tsStat = statSync(join(fakeOld, "tsconfig.base.json"));
|
|
247
280
|
if (!tsStat.isFile()) {
|
|
248
281
|
fail("Refresh turned tsconfig.base.json into a non-file. cp branching is wrong.");
|
|
249
282
|
process.exit(1);
|
|
250
283
|
}
|
|
251
|
-
const tsContent =
|
|
284
|
+
const tsContent = readFileSync(join(fakeOld, "tsconfig.base.json"), "utf8");
|
|
252
285
|
if (tsContent.includes('"old":"to-be-overwritten"')) {
|
|
253
286
|
fail("Refresh failed to overwrite tsconfig.base.json with the bundle's version.");
|
|
254
287
|
process.exit(1);
|