canary-test-cli 5.7.0 → 5.11.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/bin/canary +0 -0
- package/bin/canary.js +41 -9
- package/dist/doctor-manifest.js +307 -0
- package/dist/doctor.js +165 -0
- package/dist/engine-checks.js +320 -0
- package/dist/overlay-commands.js +390 -0
- package/dist/overlays-registry.js +162 -0
- package/dist/router.js +140 -0
- package/dist/source-spec.js +107 -0
- package/package.json +10 -3
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SourceSpecError = void 0;
|
|
4
|
+
exports.parseSource = parseSource;
|
|
5
|
+
/** Raised when a source spec does not match any accepted form. */
|
|
6
|
+
class SourceSpecError extends Error {
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "SourceSpecError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.SourceSpecError = SourceSpecError;
|
|
13
|
+
const ACCEPTED_FORMS = "accepted forms: github:owner/repo, an https/git@ URL, or a local path";
|
|
14
|
+
/** Strip a trailing `.git` and any trailing slashes from a path segment. */
|
|
15
|
+
function stripRepoSuffix(segment) {
|
|
16
|
+
return segment.replace(/\.git$/i, "").replace(/\/+$/, "");
|
|
17
|
+
}
|
|
18
|
+
/** Sanitize a raw `owner/repo` pair into `owner-repo`, rejecting empties. */
|
|
19
|
+
function nameFromOwnerRepo(owner, repo, raw) {
|
|
20
|
+
const cleanOwner = owner.trim();
|
|
21
|
+
const cleanRepo = stripRepoSuffix(repo.trim());
|
|
22
|
+
if (!cleanOwner || !cleanRepo) {
|
|
23
|
+
throw new SourceSpecError(`cannot derive a name from "${raw}" (${ACCEPTED_FORMS})`);
|
|
24
|
+
}
|
|
25
|
+
return `${cleanOwner}-${cleanRepo}`;
|
|
26
|
+
}
|
|
27
|
+
/** Extract the last two path segments (owner, repo) from a URL-ish path. */
|
|
28
|
+
function lastTwoSegments(pathPart) {
|
|
29
|
+
const segments = pathPart.split("/").filter(Boolean);
|
|
30
|
+
if (segments.length < 2) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
return [segments[segments.length - 2], segments[segments.length - 1]];
|
|
34
|
+
}
|
|
35
|
+
/** github:owner/repo shorthand. Returns null when `spec` is not this form. */
|
|
36
|
+
function parseGithubShorthand(spec, raw) {
|
|
37
|
+
if (!spec.startsWith("github:")) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const parts = spec.slice("github:".length).split("/").filter(Boolean);
|
|
41
|
+
if (parts.length !== 2) {
|
|
42
|
+
throw new SourceSpecError(`"${raw}" is not github:owner/repo (${ACCEPTED_FORMS})`);
|
|
43
|
+
}
|
|
44
|
+
const [owner, repo] = parts;
|
|
45
|
+
return {
|
|
46
|
+
cloneUrl: `https://github.com/${owner}/${stripRepoSuffix(repo)}.git`,
|
|
47
|
+
name: nameFromOwnerRepo(owner, repo, raw),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** scp-like git URL: git@host:owner/repo(.git). Returns null when not this form. */
|
|
51
|
+
function parseScpUrl(spec, raw) {
|
|
52
|
+
const scpMatch = spec.match(/^[\w.-]+@[\w.-]+:(.+)$/);
|
|
53
|
+
if (!scpMatch) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const pair = lastTwoSegments(scpMatch[1]);
|
|
57
|
+
if (!pair) {
|
|
58
|
+
throw new SourceSpecError(`cannot derive owner/repo from "${raw}" (${ACCEPTED_FORMS})`);
|
|
59
|
+
}
|
|
60
|
+
return { cloneUrl: spec, name: nameFromOwnerRepo(pair[0], pair[1], raw) };
|
|
61
|
+
}
|
|
62
|
+
/** Full URL: https://, http://, git://, ssh://. Returns null when not this form. */
|
|
63
|
+
function parseFullUrl(spec, raw) {
|
|
64
|
+
if (!/^(https?|git|ssh):\/\//i.test(spec)) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
let pathname;
|
|
68
|
+
try {
|
|
69
|
+
pathname = new URL(spec).pathname;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
throw new SourceSpecError(`"${raw}" is not a valid URL (${ACCEPTED_FORMS})`);
|
|
73
|
+
}
|
|
74
|
+
const pair = lastTwoSegments(pathname);
|
|
75
|
+
if (!pair) {
|
|
76
|
+
throw new SourceSpecError(`cannot derive owner/repo from "${raw}" (${ACCEPTED_FORMS})`);
|
|
77
|
+
}
|
|
78
|
+
return { cloneUrl: spec, name: nameFromOwnerRepo(pair[0], pair[1], raw) };
|
|
79
|
+
}
|
|
80
|
+
/** Local filesystem path (absolute, ./relative, ../relative, or ~-relative). */
|
|
81
|
+
function parseLocalPath(spec, raw) {
|
|
82
|
+
if (!(spec.startsWith("/") || spec.startsWith("./") || spec.startsWith("../") || spec.startsWith("~"))) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const basename = stripRepoSuffix(spec.split("/").filter(Boolean).pop() ?? "");
|
|
86
|
+
if (!basename) {
|
|
87
|
+
throw new SourceSpecError(`cannot derive a name from path "${raw}" (${ACCEPTED_FORMS})`);
|
|
88
|
+
}
|
|
89
|
+
return { cloneUrl: spec, name: basename };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Parse a source spec into a clone URL and overlay name. Throws
|
|
93
|
+
* {@link SourceSpecError} on anything that does not match an accepted form.
|
|
94
|
+
*/
|
|
95
|
+
function parseSource(raw) {
|
|
96
|
+
const spec = (raw ?? "").trim();
|
|
97
|
+
if (!spec) {
|
|
98
|
+
throw new SourceSpecError(`empty source (${ACCEPTED_FORMS})`);
|
|
99
|
+
}
|
|
100
|
+
for (const handler of [parseGithubShorthand, parseScpUrl, parseFullUrl, parseLocalPath]) {
|
|
101
|
+
const parsed = handler(spec, raw);
|
|
102
|
+
if (parsed) {
|
|
103
|
+
return parsed;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
throw new SourceSpecError(`unrecognized source "${raw}" (${ACCEPTED_FORMS})`);
|
|
107
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "canary-test-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.11.0",
|
|
4
4
|
"description": "Canary — AI-powered test automation agent",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -11,14 +11,21 @@
|
|
|
11
11
|
"canary": "./bin/canary.js"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"prepare": "npm run build",
|
|
14
16
|
"postinstall": "node scripts/install.js",
|
|
15
|
-
"test": "node --test scripts/__tests__
|
|
17
|
+
"test": "npm run build && node --test \"scripts/__tests__/*.test.js\""
|
|
16
18
|
},
|
|
17
19
|
"engines": {
|
|
18
20
|
"node": ">=18"
|
|
19
21
|
},
|
|
20
22
|
"files": [
|
|
21
23
|
"bin/",
|
|
24
|
+
"dist/",
|
|
22
25
|
"scripts/install.js"
|
|
23
|
-
]
|
|
26
|
+
],
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^22.0.0",
|
|
29
|
+
"typescript": "^5.6.0"
|
|
30
|
+
}
|
|
24
31
|
}
|