claudekit-cli 1.2.0 → 1.2.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/CHANGELOG.md +7 -0
- package/biome.json +1 -1
- package/dist/index.js +12002 -11997
- package/package.json +1 -1
- package/src/commands/new.ts +2 -2
- package/src/commands/update.ts +2 -2
- package/src/lib/download.ts +11 -9
- package/src/lib/github.ts +31 -14
- package/src/types.ts +2 -1
- package/src/utils/logger.ts +2 -2
- package/src/utils/safe-prompts.ts +21 -31
- package/src/utils/safe-spinner.ts +38 -0
- package/tests/lib/github-download-priority.test.ts +136 -5
- package/tests/types.test.ts +3 -0
package/package.json
CHANGED
package/src/commands/new.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { pathExists, readdir } from "fs-extra";
|
|
3
|
-
import ora from "ora";
|
|
4
3
|
import { AuthManager } from "../lib/auth.js";
|
|
5
4
|
import { DownloadManager } from "../lib/download.js";
|
|
6
5
|
import { GitHubClient } from "../lib/github.js";
|
|
@@ -9,6 +8,7 @@ import { PromptsManager } from "../lib/prompts.js";
|
|
|
9
8
|
import { AVAILABLE_KITS, type NewCommandOptions, NewCommandOptionsSchema } from "../types.js";
|
|
10
9
|
import { ConfigManager } from "../utils/config.js";
|
|
11
10
|
import { logger } from "../utils/logger.js";
|
|
11
|
+
import { createSpinner } from "../utils/safe-spinner.js";
|
|
12
12
|
|
|
13
13
|
export async function newCommand(options: NewCommandOptions): Promise<void> {
|
|
14
14
|
const prompts = new PromptsManager();
|
|
@@ -59,7 +59,7 @@ export async function newCommand(options: NewCommandOptions): Promise<void> {
|
|
|
59
59
|
const github = new GitHubClient();
|
|
60
60
|
|
|
61
61
|
// Check repository access
|
|
62
|
-
const spinner =
|
|
62
|
+
const spinner = createSpinner("Checking repository access...").start();
|
|
63
63
|
const hasAccess = await github.checkAccess(kitConfig);
|
|
64
64
|
if (!hasAccess) {
|
|
65
65
|
spinner.fail("Access denied to repository");
|
package/src/commands/update.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { pathExists } from "fs-extra";
|
|
3
|
-
import ora from "ora";
|
|
4
3
|
import { AuthManager } from "../lib/auth.js";
|
|
5
4
|
import { DownloadManager } from "../lib/download.js";
|
|
6
5
|
import { GitHubClient } from "../lib/github.js";
|
|
@@ -10,6 +9,7 @@ import { AVAILABLE_KITS, type UpdateCommandOptions, UpdateCommandOptionsSchema }
|
|
|
10
9
|
import { ConfigManager } from "../utils/config.js";
|
|
11
10
|
import { FileScanner } from "../utils/file-scanner.js";
|
|
12
11
|
import { logger } from "../utils/logger.js";
|
|
12
|
+
import { createSpinner } from "../utils/safe-spinner.js";
|
|
13
13
|
|
|
14
14
|
export async function updateCommand(options: UpdateCommandOptions): Promise<void> {
|
|
15
15
|
const prompts = new PromptsManager();
|
|
@@ -52,7 +52,7 @@ export async function updateCommand(options: UpdateCommandOptions): Promise<void
|
|
|
52
52
|
const github = new GitHubClient();
|
|
53
53
|
|
|
54
54
|
// Check repository access
|
|
55
|
-
const spinner =
|
|
55
|
+
const spinner = createSpinner("Checking repository access...").start();
|
|
56
56
|
const hasAccess = await github.checkAccess(kitConfig);
|
|
57
57
|
if (!hasAccess) {
|
|
58
58
|
spinner.fail("Access denied to repository");
|
package/src/lib/download.ts
CHANGED
|
@@ -6,7 +6,6 @@ import { pipeline } from "node:stream";
|
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import cliProgress from "cli-progress";
|
|
8
8
|
import ignore from "ignore";
|
|
9
|
-
import ora from "ora";
|
|
10
9
|
import * as tar from "tar";
|
|
11
10
|
import unzipper from "unzipper";
|
|
12
11
|
import {
|
|
@@ -16,6 +15,7 @@ import {
|
|
|
16
15
|
type GitHubReleaseAsset,
|
|
17
16
|
} from "../types.js";
|
|
18
17
|
import { logger } from "../utils/logger.js";
|
|
18
|
+
import { createSpinner } from "../utils/safe-spinner.js";
|
|
19
19
|
|
|
20
20
|
const streamPipeline = promisify(pipeline);
|
|
21
21
|
|
|
@@ -55,11 +55,11 @@ export class DownloadManager {
|
|
|
55
55
|
|
|
56
56
|
logger.info(`Downloading ${asset.name} (${this.formatBytes(asset.size)})...`);
|
|
57
57
|
|
|
58
|
-
// Create progress bar
|
|
58
|
+
// Create progress bar with simple ASCII characters
|
|
59
59
|
const progressBar = new cliProgress.SingleBar({
|
|
60
60
|
format: "Progress |{bar}| {percentage}% | {value}/{total} MB",
|
|
61
|
-
barCompleteChar: "
|
|
62
|
-
barIncompleteChar: "
|
|
61
|
+
barCompleteChar: "=",
|
|
62
|
+
barIncompleteChar: "-",
|
|
63
63
|
hideCursor: true,
|
|
64
64
|
});
|
|
65
65
|
|
|
@@ -137,7 +137,9 @@ export class DownloadManager {
|
|
|
137
137
|
// Add authentication for GitHub API URLs
|
|
138
138
|
if (token && url.includes("api.github.com")) {
|
|
139
139
|
headers.Authorization = `Bearer ${token}`;
|
|
140
|
-
|
|
140
|
+
// Use application/octet-stream for asset downloads (not vnd.github+json)
|
|
141
|
+
headers.Accept = "application/octet-stream";
|
|
142
|
+
headers["X-GitHub-Api-Version"] = "2022-11-28";
|
|
141
143
|
} else {
|
|
142
144
|
headers.Accept = "application/octet-stream";
|
|
143
145
|
}
|
|
@@ -151,13 +153,13 @@ export class DownloadManager {
|
|
|
151
153
|
const totalSize = size || Number(response.headers.get("content-length")) || 0;
|
|
152
154
|
let downloadedSize = 0;
|
|
153
155
|
|
|
154
|
-
// Create progress bar only if we know the size
|
|
156
|
+
// Create progress bar only if we know the size (using simple ASCII characters)
|
|
155
157
|
const progressBar =
|
|
156
158
|
totalSize > 0
|
|
157
159
|
? new cliProgress.SingleBar({
|
|
158
160
|
format: "Progress |{bar}| {percentage}% | {value}/{total} MB",
|
|
159
|
-
barCompleteChar: "
|
|
160
|
-
barIncompleteChar: "
|
|
161
|
+
barCompleteChar: "=",
|
|
162
|
+
barIncompleteChar: "-",
|
|
161
163
|
hideCursor: true,
|
|
162
164
|
})
|
|
163
165
|
: null;
|
|
@@ -207,7 +209,7 @@ export class DownloadManager {
|
|
|
207
209
|
destDir: string,
|
|
208
210
|
archiveType?: ArchiveType,
|
|
209
211
|
): Promise<void> {
|
|
210
|
-
const spinner =
|
|
212
|
+
const spinner = createSpinner("Extracting files...").start();
|
|
211
213
|
|
|
212
214
|
try {
|
|
213
215
|
// Detect archive type from filename if not provided
|
package/src/lib/github.ts
CHANGED
|
@@ -159,7 +159,7 @@ export class GitHubClient {
|
|
|
159
159
|
* Get downloadable asset or source code URL from release
|
|
160
160
|
* Priority:
|
|
161
161
|
* 1. "ClaudeKit Engineer Package" or "ClaudeKit Marketing Package" zip file
|
|
162
|
-
* 2. Other custom uploaded assets (.tar.gz, .tgz, .zip)
|
|
162
|
+
* 2. Other custom uploaded assets (.tar.gz, .tgz, .zip) excluding "Source code" archives
|
|
163
163
|
* 3. GitHub's automatic tarball URL
|
|
164
164
|
*/
|
|
165
165
|
static getDownloadableAsset(release: GitHubRelease): {
|
|
@@ -168,41 +168,58 @@ export class GitHubClient {
|
|
|
168
168
|
name: string;
|
|
169
169
|
size?: number;
|
|
170
170
|
} {
|
|
171
|
+
// Log all available assets for debugging
|
|
172
|
+
logger.debug(`Available assets for ${release.tag_name}:`);
|
|
173
|
+
if (release.assets.length === 0) {
|
|
174
|
+
logger.debug(" No custom assets found");
|
|
175
|
+
} else {
|
|
176
|
+
release.assets.forEach((asset, index) => {
|
|
177
|
+
logger.debug(` ${index + 1}. ${asset.name} (${(asset.size / 1024 / 1024).toFixed(2)} MB)`);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
171
181
|
// First priority: Look for official ClaudeKit package assets
|
|
172
|
-
const packageAsset = release.assets.find(
|
|
173
|
-
(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
182
|
+
const packageAsset = release.assets.find((a) => {
|
|
183
|
+
const nameLower = a.name.toLowerCase();
|
|
184
|
+
return (
|
|
185
|
+
nameLower.includes("claudekit") &&
|
|
186
|
+
nameLower.includes("package") &&
|
|
187
|
+
nameLower.endsWith(".zip")
|
|
188
|
+
);
|
|
189
|
+
});
|
|
178
190
|
|
|
179
191
|
if (packageAsset) {
|
|
180
|
-
logger.debug(
|
|
192
|
+
logger.debug(`✓ Selected ClaudeKit package asset: ${packageAsset.name}`);
|
|
181
193
|
return {
|
|
182
194
|
type: "asset",
|
|
183
|
-
url: packageAsset.
|
|
195
|
+
url: packageAsset.url, // Use API endpoint for authenticated downloads
|
|
184
196
|
name: packageAsset.name,
|
|
185
197
|
size: packageAsset.size,
|
|
186
198
|
};
|
|
187
199
|
}
|
|
188
200
|
|
|
189
|
-
|
|
201
|
+
logger.debug("⚠ No ClaudeKit package asset found, checking for other custom assets...");
|
|
202
|
+
|
|
203
|
+
// Second priority: Look for any custom uploaded assets (excluding GitHub's automatic source code archives)
|
|
190
204
|
const customAsset = release.assets.find(
|
|
191
|
-
(a) =>
|
|
205
|
+
(a) =>
|
|
206
|
+
(a.name.endsWith(".tar.gz") || a.name.endsWith(".tgz") || a.name.endsWith(".zip")) &&
|
|
207
|
+
!a.name.toLowerCase().startsWith("source") &&
|
|
208
|
+
!a.name.toLowerCase().includes("source code"),
|
|
192
209
|
);
|
|
193
210
|
|
|
194
211
|
if (customAsset) {
|
|
195
|
-
logger.debug(
|
|
212
|
+
logger.debug(`✓ Selected custom asset: ${customAsset.name}`);
|
|
196
213
|
return {
|
|
197
214
|
type: "asset",
|
|
198
|
-
url: customAsset.
|
|
215
|
+
url: customAsset.url, // Use API endpoint for authenticated downloads
|
|
199
216
|
name: customAsset.name,
|
|
200
217
|
size: customAsset.size,
|
|
201
218
|
};
|
|
202
219
|
}
|
|
203
220
|
|
|
204
221
|
// Fall back to GitHub's automatic tarball
|
|
205
|
-
logger.debug("
|
|
222
|
+
logger.debug("⚠ No custom assets found, falling back to GitHub automatic tarball");
|
|
206
223
|
return {
|
|
207
224
|
type: "tarball",
|
|
208
225
|
url: release.tarball_url,
|
package/src/types.ts
CHANGED
|
@@ -46,7 +46,8 @@ export type Config = z.infer<typeof ConfigSchema>;
|
|
|
46
46
|
export const GitHubReleaseAssetSchema = z.object({
|
|
47
47
|
id: z.number(),
|
|
48
48
|
name: z.string(),
|
|
49
|
-
|
|
49
|
+
url: z.string().url(), // API endpoint for authenticated downloads
|
|
50
|
+
browser_download_url: z.string().url(), // Direct download URL (public only)
|
|
50
51
|
size: z.number(),
|
|
51
52
|
content_type: z.string(),
|
|
52
53
|
});
|
package/src/utils/logger.ts
CHANGED
|
@@ -4,9 +4,9 @@ import pc from "picocolors";
|
|
|
4
4
|
// Use ASCII-safe symbols to avoid unicode rendering issues in certain terminals
|
|
5
5
|
const symbols = {
|
|
6
6
|
info: "[i]",
|
|
7
|
-
success: "[
|
|
7
|
+
success: "[+]",
|
|
8
8
|
warning: "[!]",
|
|
9
|
-
error: "[
|
|
9
|
+
error: "[x]",
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
interface LogContext {
|
|
@@ -1,53 +1,43 @@
|
|
|
1
|
-
import
|
|
1
|
+
import picocolors from "picocolors";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Safe wrapper around clack prompts that
|
|
5
|
-
*
|
|
4
|
+
* Safe wrapper around clack prompts that uses simple ASCII characters
|
|
5
|
+
* instead of unicode box drawing to avoid rendering issues.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
// Store original methods
|
|
9
|
-
const originalIntro = clack.intro;
|
|
10
|
-
const originalOutro = clack.outro;
|
|
11
|
-
const originalNote = clack.note;
|
|
12
|
-
|
|
13
8
|
/**
|
|
14
|
-
*
|
|
9
|
+
* Simple intro with ASCII characters
|
|
15
10
|
*/
|
|
16
11
|
export function intro(message: string): void {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// Fallback to simple console log if clack fails
|
|
21
|
-
console.log(`\n=== ${message} ===\n`);
|
|
22
|
-
}
|
|
12
|
+
console.log();
|
|
13
|
+
console.log(picocolors.cyan(`> ${message}`));
|
|
14
|
+
console.log();
|
|
23
15
|
}
|
|
24
16
|
|
|
25
17
|
/**
|
|
26
|
-
*
|
|
18
|
+
* Simple outro with ASCII characters
|
|
27
19
|
*/
|
|
28
20
|
export function outro(message: string): void {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// Fallback to simple console log if clack fails
|
|
33
|
-
console.log(`\n=== ${message} ===\n`);
|
|
34
|
-
}
|
|
21
|
+
console.log();
|
|
22
|
+
console.log(picocolors.green(`[OK] ${message}`));
|
|
23
|
+
console.log();
|
|
35
24
|
}
|
|
36
25
|
|
|
37
26
|
/**
|
|
38
|
-
*
|
|
27
|
+
* Simple note with ASCII box drawing
|
|
39
28
|
*/
|
|
40
29
|
export function note(message: string, title?: string): void {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// Fallback to simple console log if clack fails
|
|
45
|
-
if (title) {
|
|
46
|
-
console.log(`\n--- ${title} ---`);
|
|
47
|
-
}
|
|
48
|
-
console.log(message);
|
|
30
|
+
console.log();
|
|
31
|
+
if (title) {
|
|
32
|
+
console.log(picocolors.cyan(` ${title}:`));
|
|
49
33
|
console.log();
|
|
50
34
|
}
|
|
35
|
+
// Split message into lines and indent each
|
|
36
|
+
const lines = message.split("\n");
|
|
37
|
+
for (const line of lines) {
|
|
38
|
+
console.log(` ${line}`);
|
|
39
|
+
}
|
|
40
|
+
console.log();
|
|
51
41
|
}
|
|
52
42
|
|
|
53
43
|
// Re-export other clack functions unchanged
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import ora, { type Ora, type Options } from "ora";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create a spinner with simple ASCII characters to avoid unicode rendering issues
|
|
5
|
+
*/
|
|
6
|
+
export function createSpinner(options: string | Options): Ora {
|
|
7
|
+
const spinnerOptions: Options = typeof options === "string" ? { text: options } : options;
|
|
8
|
+
|
|
9
|
+
const spinner = ora({
|
|
10
|
+
...spinnerOptions,
|
|
11
|
+
// Use simple ASCII spinner instead of unicode
|
|
12
|
+
spinner: "dots",
|
|
13
|
+
// Override symbols to use ASCII
|
|
14
|
+
prefixText: "",
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Override succeed and fail methods to use ASCII symbols
|
|
18
|
+
spinner.succeed = (text?: string) => {
|
|
19
|
+
spinner.stopAndPersist({
|
|
20
|
+
symbol: "[+]",
|
|
21
|
+
text: text || spinner.text,
|
|
22
|
+
});
|
|
23
|
+
return spinner;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
spinner.fail = (text?: string) => {
|
|
27
|
+
spinner.stopAndPersist({
|
|
28
|
+
symbol: "[x]",
|
|
29
|
+
text: text || spinner.text,
|
|
30
|
+
});
|
|
31
|
+
return spinner;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return spinner;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Re-export Ora type for convenience
|
|
38
|
+
export type { Ora } from "ora";
|
|
@@ -17,6 +17,7 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
17
17
|
{
|
|
18
18
|
id: 1,
|
|
19
19
|
name: "other-file.tar.gz",
|
|
20
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/1",
|
|
20
21
|
browser_download_url: "https://github.com/test/other-file.tar.gz",
|
|
21
22
|
size: 1024,
|
|
22
23
|
content_type: "application/gzip",
|
|
@@ -24,6 +25,7 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
24
25
|
{
|
|
25
26
|
id: 2,
|
|
26
27
|
name: "ClaudeKit-Engineer-Package.zip",
|
|
28
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/2",
|
|
27
29
|
browser_download_url: "https://github.com/test/claudekit-package.zip",
|
|
28
30
|
size: 2048,
|
|
29
31
|
content_type: "application/zip",
|
|
@@ -35,7 +37,7 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
35
37
|
|
|
36
38
|
expect(result.type).toBe("asset");
|
|
37
39
|
expect(result.name).toBe("ClaudeKit-Engineer-Package.zip");
|
|
38
|
-
expect(result.url).toBe("https://github.com/test/
|
|
40
|
+
expect(result.url).toBe("https://api.github.com/repos/test/repo/releases/assets/2");
|
|
39
41
|
expect(result.size).toBe(2048);
|
|
40
42
|
});
|
|
41
43
|
|
|
@@ -52,6 +54,7 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
52
54
|
{
|
|
53
55
|
id: 1,
|
|
54
56
|
name: "random.zip",
|
|
57
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/1",
|
|
55
58
|
browser_download_url: "https://github.com/test/random.zip",
|
|
56
59
|
size: 512,
|
|
57
60
|
content_type: "application/zip",
|
|
@@ -59,6 +62,7 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
59
62
|
{
|
|
60
63
|
id: 2,
|
|
61
64
|
name: "ClaudeKit-Marketing-Package.zip",
|
|
65
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/2",
|
|
62
66
|
browser_download_url: "https://github.com/test/marketing-package.zip",
|
|
63
67
|
size: 2048,
|
|
64
68
|
content_type: "application/zip",
|
|
@@ -70,7 +74,7 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
70
74
|
|
|
71
75
|
expect(result.type).toBe("asset");
|
|
72
76
|
expect(result.name).toBe("ClaudeKit-Marketing-Package.zip");
|
|
73
|
-
expect(result.url).toBe("https://github.com/test/
|
|
77
|
+
expect(result.url).toBe("https://api.github.com/repos/test/repo/releases/assets/2");
|
|
74
78
|
});
|
|
75
79
|
|
|
76
80
|
test("should match ClaudeKit package case-insensitively", () => {
|
|
@@ -86,6 +90,7 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
86
90
|
{
|
|
87
91
|
id: 1,
|
|
88
92
|
name: "claudekit-engineer-package.zip",
|
|
93
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/1",
|
|
89
94
|
browser_download_url: "https://github.com/test/package.zip",
|
|
90
95
|
size: 2048,
|
|
91
96
|
content_type: "application/zip",
|
|
@@ -111,8 +116,9 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
111
116
|
assets: [
|
|
112
117
|
{
|
|
113
118
|
id: 1,
|
|
114
|
-
name: "
|
|
115
|
-
|
|
119
|
+
name: "release-package.zip",
|
|
120
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/1",
|
|
121
|
+
browser_download_url: "https://github.com/test/release.zip",
|
|
116
122
|
size: 1024,
|
|
117
123
|
content_type: "application/zip",
|
|
118
124
|
},
|
|
@@ -122,7 +128,7 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
122
128
|
const result = GitHubClient.getDownloadableAsset(release);
|
|
123
129
|
|
|
124
130
|
expect(result.type).toBe("asset");
|
|
125
|
-
expect(result.name).toBe("
|
|
131
|
+
expect(result.name).toBe("release-package.zip");
|
|
126
132
|
});
|
|
127
133
|
|
|
128
134
|
test("should fallback to tar.gz files if no zip found", () => {
|
|
@@ -138,6 +144,7 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
138
144
|
{
|
|
139
145
|
id: 1,
|
|
140
146
|
name: "release.tar.gz",
|
|
147
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/1",
|
|
141
148
|
browser_download_url: "https://github.com/test/release.tar.gz",
|
|
142
149
|
size: 1024,
|
|
143
150
|
content_type: "application/gzip",
|
|
@@ -164,6 +171,7 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
164
171
|
{
|
|
165
172
|
id: 1,
|
|
166
173
|
name: "release.tgz",
|
|
174
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/1",
|
|
167
175
|
browser_download_url: "https://github.com/test/release.tgz",
|
|
168
176
|
size: 1024,
|
|
169
177
|
content_type: "application/gzip",
|
|
@@ -210,6 +218,7 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
210
218
|
{
|
|
211
219
|
id: 1,
|
|
212
220
|
name: "README.md",
|
|
221
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/1",
|
|
213
222
|
browser_download_url: "https://github.com/test/README.md",
|
|
214
223
|
size: 128,
|
|
215
224
|
content_type: "text/markdown",
|
|
@@ -217,6 +226,7 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
217
226
|
{
|
|
218
227
|
id: 2,
|
|
219
228
|
name: "checksums.txt",
|
|
229
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/2",
|
|
220
230
|
browser_download_url: "https://github.com/test/checksums.txt",
|
|
221
231
|
size: 64,
|
|
222
232
|
content_type: "text/plain",
|
|
@@ -243,6 +253,7 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
243
253
|
{
|
|
244
254
|
id: 1,
|
|
245
255
|
name: "source.tar.gz",
|
|
256
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/1",
|
|
246
257
|
browser_download_url: "https://github.com/test/source.tar.gz",
|
|
247
258
|
size: 5000,
|
|
248
259
|
content_type: "application/gzip",
|
|
@@ -250,6 +261,7 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
250
261
|
{
|
|
251
262
|
id: 2,
|
|
252
263
|
name: "docs.zip",
|
|
264
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/2",
|
|
253
265
|
browser_download_url: "https://github.com/test/docs.zip",
|
|
254
266
|
size: 3000,
|
|
255
267
|
content_type: "application/zip",
|
|
@@ -257,6 +269,7 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
257
269
|
{
|
|
258
270
|
id: 3,
|
|
259
271
|
name: "ClaudeKit-Engineer-Package.zip",
|
|
272
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/3",
|
|
260
273
|
browser_download_url: "https://github.com/test/package.zip",
|
|
261
274
|
size: 2000,
|
|
262
275
|
content_type: "application/zip",
|
|
@@ -285,6 +298,7 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
285
298
|
{
|
|
286
299
|
id: 1,
|
|
287
300
|
name: "claudekit_marketing_package.zip",
|
|
301
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/1",
|
|
288
302
|
browser_download_url: "https://github.com/test/package.zip",
|
|
289
303
|
size: 2000,
|
|
290
304
|
content_type: "application/zip",
|
|
@@ -297,5 +311,122 @@ describe("GitHubClient - Asset Download Priority", () => {
|
|
|
297
311
|
expect(result.type).toBe("asset");
|
|
298
312
|
expect(result.name).toBe("claudekit_marketing_package.zip");
|
|
299
313
|
});
|
|
314
|
+
|
|
315
|
+
test("should handle assets with spaces in name", () => {
|
|
316
|
+
const release: GitHubRelease = {
|
|
317
|
+
id: 1,
|
|
318
|
+
tag_name: "v1.4.0",
|
|
319
|
+
name: "Release 1.4.0",
|
|
320
|
+
draft: false,
|
|
321
|
+
prerelease: false,
|
|
322
|
+
tarball_url: "https://api.github.com/repos/test/repo/tarball/v1.4.0",
|
|
323
|
+
zipball_url: "https://api.github.com/repos/test/repo/zipball/v1.4.0",
|
|
324
|
+
assets: [
|
|
325
|
+
{
|
|
326
|
+
id: 1,
|
|
327
|
+
name: "Changelog",
|
|
328
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/1",
|
|
329
|
+
browser_download_url: "https://github.com/test/changelog",
|
|
330
|
+
size: 7979,
|
|
331
|
+
content_type: "text/plain",
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
id: 2,
|
|
335
|
+
name: "ClaudeKit Engineer Package.zip",
|
|
336
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/2",
|
|
337
|
+
browser_download_url: "https://github.com/test/package.zip",
|
|
338
|
+
size: 3334963,
|
|
339
|
+
content_type: "application/zip",
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
const result = GitHubClient.getDownloadableAsset(release);
|
|
345
|
+
|
|
346
|
+
expect(result.type).toBe("asset");
|
|
347
|
+
expect(result.name).toBe("ClaudeKit Engineer Package.zip");
|
|
348
|
+
expect(result.size).toBe(3334963);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test("should exclude assets named 'Source code' or starting with 'source'", () => {
|
|
352
|
+
const release: GitHubRelease = {
|
|
353
|
+
id: 1,
|
|
354
|
+
tag_name: "v1.0.0",
|
|
355
|
+
name: "Release 1.0.0",
|
|
356
|
+
draft: false,
|
|
357
|
+
prerelease: false,
|
|
358
|
+
tarball_url: "https://api.github.com/repos/test/repo/tarball/v1.0.0",
|
|
359
|
+
zipball_url: "https://api.github.com/repos/test/repo/zipball/v1.0.0",
|
|
360
|
+
assets: [
|
|
361
|
+
{
|
|
362
|
+
id: 1,
|
|
363
|
+
name: "Source code.zip",
|
|
364
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/1",
|
|
365
|
+
browser_download_url: "https://github.com/test/source.zip",
|
|
366
|
+
size: 5000,
|
|
367
|
+
content_type: "application/zip",
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
id: 2,
|
|
371
|
+
name: "source-archive.tar.gz",
|
|
372
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/2",
|
|
373
|
+
browser_download_url: "https://github.com/test/source.tar.gz",
|
|
374
|
+
size: 4500,
|
|
375
|
+
content_type: "application/gzip",
|
|
376
|
+
},
|
|
377
|
+
],
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
const result = GitHubClient.getDownloadableAsset(release);
|
|
381
|
+
|
|
382
|
+
// Should fall back to tarball instead of using "Source code" assets
|
|
383
|
+
expect(result.type).toBe("tarball");
|
|
384
|
+
expect(result.url).toBe("https://api.github.com/repos/test/repo/tarball/v1.0.0");
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
test("should prioritize ClaudeKit package over source code archives", () => {
|
|
388
|
+
const release: GitHubRelease = {
|
|
389
|
+
id: 1,
|
|
390
|
+
tag_name: "v1.4.0",
|
|
391
|
+
name: "Release 1.4.0",
|
|
392
|
+
draft: false,
|
|
393
|
+
prerelease: false,
|
|
394
|
+
tarball_url: "https://api.github.com/repos/test/repo/tarball/v1.4.0",
|
|
395
|
+
zipball_url: "https://api.github.com/repos/test/repo/zipball/v1.4.0",
|
|
396
|
+
assets: [
|
|
397
|
+
{
|
|
398
|
+
id: 1,
|
|
399
|
+
name: "Source code.zip",
|
|
400
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/1",
|
|
401
|
+
browser_download_url: "https://github.com/test/source.zip",
|
|
402
|
+
size: 5000,
|
|
403
|
+
content_type: "application/zip",
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
id: 2,
|
|
407
|
+
name: "ClaudeKit Engineer Package.zip",
|
|
408
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/2",
|
|
409
|
+
browser_download_url: "https://github.com/test/package.zip",
|
|
410
|
+
size: 3334963,
|
|
411
|
+
content_type: "application/zip",
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
id: 3,
|
|
415
|
+
name: "Source code.tar.gz",
|
|
416
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/3",
|
|
417
|
+
browser_download_url: "https://github.com/test/source.tar.gz",
|
|
418
|
+
size: 4500,
|
|
419
|
+
content_type: "application/gzip",
|
|
420
|
+
},
|
|
421
|
+
],
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const result = GitHubClient.getDownloadableAsset(release);
|
|
425
|
+
|
|
426
|
+
// Should pick the ClaudeKit package and ignore source code archives
|
|
427
|
+
expect(result.type).toBe("asset");
|
|
428
|
+
expect(result.name).toBe("ClaudeKit Engineer Package.zip");
|
|
429
|
+
expect(result.size).toBe(3334963);
|
|
430
|
+
});
|
|
300
431
|
});
|
|
301
432
|
});
|
package/tests/types.test.ts
CHANGED
|
@@ -108,6 +108,7 @@ describe("Types and Schemas", () => {
|
|
|
108
108
|
const asset = {
|
|
109
109
|
id: 123,
|
|
110
110
|
name: "release.tar.gz",
|
|
111
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/123",
|
|
111
112
|
browser_download_url: "https://github.com/test/release.tar.gz",
|
|
112
113
|
size: 1024,
|
|
113
114
|
content_type: "application/gzip",
|
|
@@ -122,6 +123,7 @@ describe("Types and Schemas", () => {
|
|
|
122
123
|
const asset = {
|
|
123
124
|
id: 123,
|
|
124
125
|
name: "release.tar.gz",
|
|
126
|
+
url: "not-a-url",
|
|
125
127
|
browser_download_url: "not-a-url",
|
|
126
128
|
size: 1024,
|
|
127
129
|
content_type: "application/gzip",
|
|
@@ -150,6 +152,7 @@ describe("Types and Schemas", () => {
|
|
|
150
152
|
{
|
|
151
153
|
id: 123,
|
|
152
154
|
name: "release.tar.gz",
|
|
155
|
+
url: "https://api.github.com/repos/test/repo/releases/assets/123",
|
|
153
156
|
browser_download_url: "https://github.com/test/release.tar.gz",
|
|
154
157
|
size: 1024,
|
|
155
158
|
content_type: "application/gzip",
|