@gonkagate/claude-code 0.1.0 → 0.1.2
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 +25 -0
- package/README.md +23 -15
- package/dist/install/backup.js +2 -1
- package/dist/install/backup.js.map +1 -1
- package/dist/install/local-git-ignore.js +91 -9
- package/dist/install/local-git-ignore.js.map +1 -1
- package/docs/how-it-works.md +4 -2
- package/docs/security.md +6 -2
- package/docs/troubleshooting.md +16 -0
- package/package.json +13 -9
- package/scripts/run-tests.mjs +49 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,10 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Improved npm package metadata and README copy for better package-page clarity, discovery, and onboarding.
|
|
5
6
|
- Added a curated model registry and model picker to the public installer flow.
|
|
6
7
|
- Added `--model <model-key>` and `--model=<model-key>` support for curated non-secret model selection.
|
|
7
8
|
- Kept the public Claude Code contract tight by limiting model selection to the curated GonkaGate registry only.
|
|
8
9
|
- Updated docs and tests for model selection while preserving local git ignore protection and owner-only settings file permissions.
|
|
10
|
+
- Hardened local scope against symlinked `.claude` paths that could redirect secret writes outside the ignored settings path.
|
|
11
|
+
- Extended local git protection to ignore timestamped backup files for `.claude/settings.local.json`.
|
|
12
|
+
- Normalized backup permissions to owner-only mode so secret-bearing backups are not left world-readable.
|
|
13
|
+
- Made local scope fail closed if `.claude/settings.local.json` is already tracked by git before secrets are written.
|
|
14
|
+
- Hardened local scope against symlinked path components anywhere between the repo root and the target settings file.
|
|
15
|
+
- Pinned GitHub Actions workflows to immutable commit SHAs for release pipeline hardening.
|
|
16
|
+
- Restored automated npm publish dispatch after Release Please creates a new release tag.
|
|
17
|
+
- Made publish reruns skip versions that are already present on npm instead of failing with a duplicate-version error.
|
|
18
|
+
|
|
19
|
+
## [0.1.2](https://github.com/GonkaGate/gonkagate-claude-code/compare/v0.1.1...v0.1.2) (2026-03-31)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Bug Fixes
|
|
23
|
+
|
|
24
|
+
* restore automated npm publishes ([42bde87](https://github.com/GonkaGate/gonkagate-claude-code/commit/42bde8795e264c8950017aa5acff0475b4fb0c8e))
|
|
25
|
+
* restore automated npm publishes ([52fcd8e](https://github.com/GonkaGate/gonkagate-claude-code/commit/52fcd8e428ccae967871aba800344dd786a048ce))
|
|
26
|
+
|
|
27
|
+
## [0.1.1](https://github.com/GonkaGate/gonkagate-claude-code/compare/v0.1.0...v0.1.1) (2026-03-31)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Bug Fixes
|
|
31
|
+
|
|
32
|
+
* stop duplicate npm publishes ([1ac876a](https://github.com/GonkaGate/gonkagate-claude-code/commit/1ac876a4fb1c99bd1a1cc892e38ee2c7df3be799))
|
|
33
|
+
* stop duplicate npm publishes ([58d022f](https://github.com/GonkaGate/gonkagate-claude-code/commit/58d022ff2486fa6156badc791cb236f580b68a9c))
|
|
9
34
|
|
|
10
35
|
## 0.1.0
|
|
11
36
|
|
package/README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
# gonkagate
|
|
1
|
+
# @gonkagate/claude-code
|
|
2
2
|
|
|
3
|
-
Set up Claude Code to use GonkaGate in one
|
|
3
|
+
Set up Claude Code to use GonkaGate in one `npx` command.
|
|
4
4
|
|
|
5
|
-
This
|
|
5
|
+
This CLI installer is for developers who already have local `Claude Code` and want to use it with GonkaGate, the gateway to Gonka Network, without editing shell profiles, exporting long env var blocks, or writing `.env` files by hand.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Under the hood it configures Claude Code to use GonkaGate's Anthropic-compatible endpoint at `https://api.gonkagate.com`.
|
|
8
|
+
|
|
9
|
+
It does not install `Claude Code` itself. It configures an existing local Claude Code install.
|
|
8
10
|
|
|
9
11
|
## Quick Start
|
|
10
12
|
|
|
@@ -12,9 +14,11 @@ It does not install `Claude Code` itself. It sets up an existing local Claude Co
|
|
|
12
14
|
npx @gonkagate/claude-code
|
|
13
15
|
```
|
|
14
16
|
|
|
17
|
+
Need an API key first? [Create one on GonkaGate](https://gonkagate.com/en).
|
|
18
|
+
|
|
15
19
|
You will be asked for:
|
|
16
20
|
|
|
17
|
-
- your GonkaGate API key (`gp-...`)
|
|
21
|
+
- your GonkaGate API key (`gp-...`) in a hidden interactive prompt
|
|
18
22
|
- a model from the supported GonkaGate list
|
|
19
23
|
- setup scope: `user` or `local`
|
|
20
24
|
|
|
@@ -24,6 +28,12 @@ You need:
|
|
|
24
28
|
- Node.js 18+
|
|
25
29
|
- a GonkaGate API key
|
|
26
30
|
|
|
31
|
+
## Supported Model
|
|
32
|
+
|
|
33
|
+
Current public Claude Code model in the curated registry:
|
|
34
|
+
|
|
35
|
+
- `qwen3-235b` -> `qwen/qwen3-235b-a22b-instruct-2507-fp8`
|
|
36
|
+
|
|
27
37
|
## What It Does
|
|
28
38
|
|
|
29
39
|
The tool writes Claude Code settings so you can keep running `claude` normally afterward.
|
|
@@ -41,7 +51,10 @@ It also:
|
|
|
41
51
|
- preserves unrelated Claude Code settings
|
|
42
52
|
- creates a backup before overwriting an existing settings file
|
|
43
53
|
- writes settings files with owner-only permissions
|
|
44
|
-
-
|
|
54
|
+
- writes backup files with owner-only permissions
|
|
55
|
+
- adds `.claude/settings.local.json` and local backup files to `.git/info/exclude` for local setup inside a git repo
|
|
56
|
+
- refuses local setup if `.claude/settings.local.json` is already tracked by git
|
|
57
|
+
- refuses local setup if the target path traverses a symlinked path component, or if `.claude` / the local settings file is a symlink
|
|
45
58
|
|
|
46
59
|
## Fixed GonkaGate Setup
|
|
47
60
|
|
|
@@ -53,12 +66,6 @@ These parts are intentionally fixed:
|
|
|
53
66
|
|
|
54
67
|
This tool does not ask for a custom base URL and does not accept arbitrary custom model IDs.
|
|
55
68
|
|
|
56
|
-
## Supported Model
|
|
57
|
-
|
|
58
|
-
Current public Claude Code model:
|
|
59
|
-
|
|
60
|
-
- `qwen3-235b` -> `qwen/qwen3-235b-a22b-instruct-2507-fp8`
|
|
61
|
-
|
|
62
69
|
The selected model is written into all Claude Code model env vars used by this setup flow.
|
|
63
70
|
|
|
64
71
|
## Verify
|
|
@@ -73,6 +80,7 @@ After setup:
|
|
|
73
80
|
## What This Tool Does Not Do
|
|
74
81
|
|
|
75
82
|
- It does not configure `claude.ai`
|
|
83
|
+
- It does not install `Claude Code` itself
|
|
76
84
|
- It does not edit `.zshrc`, `.bashrc`, PowerShell profiles, or other shell startup files
|
|
77
85
|
- It does not write `.env` files
|
|
78
86
|
- It does not support arbitrary custom model IDs
|
|
@@ -80,9 +88,9 @@ After setup:
|
|
|
80
88
|
|
|
81
89
|
## Need Help?
|
|
82
90
|
|
|
83
|
-
- Troubleshooting: [docs/troubleshooting.md](docs/troubleshooting.md)
|
|
84
|
-
- Security notes: [docs/security.md](docs/security.md)
|
|
85
|
-
- Internal behavior: [docs/how-it-works.md](docs/how-it-works.md)
|
|
91
|
+
- Troubleshooting: [docs/troubleshooting.md](https://github.com/GonkaGate/gonkagate-claude-code/blob/main/docs/troubleshooting.md)
|
|
92
|
+
- Security notes: [docs/security.md](https://github.com/GonkaGate/gonkagate-claude-code/blob/main/docs/security.md)
|
|
93
|
+
- Internal behavior: [docs/how-it-works.md](https://github.com/GonkaGate/gonkagate-claude-code/blob/main/docs/how-it-works.md)
|
|
86
94
|
|
|
87
95
|
## Development
|
|
88
96
|
|
package/dist/install/backup.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { copyFile } from "node:fs/promises";
|
|
1
|
+
import { chmod, copyFile } from "node:fs/promises";
|
|
2
2
|
function toBackupSuffix(timestamp = new Date()) {
|
|
3
3
|
return timestamp.toISOString().replace(/[:.]/g, "-");
|
|
4
4
|
}
|
|
5
5
|
export async function createBackup(filePath) {
|
|
6
6
|
const backupPath = `${filePath}.backup-${toBackupSuffix()}`;
|
|
7
7
|
await copyFile(filePath, backupPath);
|
|
8
|
+
await chmod(backupPath, 0o600);
|
|
8
9
|
return backupPath;
|
|
9
10
|
}
|
|
10
11
|
//# sourceMappingURL=backup.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/install/backup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/install/backup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEnD,SAAS,cAAc,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE;IAC5C,OAAO,SAAS,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB;IACjD,MAAM,UAAU,GAAG,GAAG,QAAQ,WAAW,cAAc,EAAE,EAAE,CAAC;IAC5D,MAAM,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACrC,MAAM,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC/B,OAAO,UAAU,CAAC;AACpB,CAAC"}
|
|
@@ -1,31 +1,104 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { access, lstat, mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
const execFileAsync = promisify(execFile);
|
|
3
6
|
export async function ensureLocalSettingsIgnored(targetPath) {
|
|
4
7
|
const gitContext = await findGitContext(path.dirname(targetPath));
|
|
8
|
+
await assertSafeLocalSettingsTarget(targetPath, gitContext?.repoRoot);
|
|
5
9
|
if (!gitContext) {
|
|
6
10
|
return;
|
|
7
11
|
}
|
|
8
|
-
const relativeTargetPath =
|
|
9
|
-
|
|
10
|
-
throw new Error("Expected local Claude Code settings to stay inside the current git repository.");
|
|
11
|
-
}
|
|
12
|
+
const relativeTargetPath = requireRepoRelativePath(targetPath, gitContext.repoRoot);
|
|
13
|
+
await assertTargetIsNotTracked(relativeTargetPath, gitContext.repoRoot);
|
|
12
14
|
const normalizedRelativePath = relativeTargetPath.split(path.sep).join("/");
|
|
13
|
-
const
|
|
15
|
+
const ignoreEntries = [
|
|
16
|
+
`/${normalizedRelativePath}`,
|
|
17
|
+
`/${normalizedRelativePath}.backup-*`
|
|
18
|
+
];
|
|
14
19
|
const excludePath = path.join(gitContext.gitDir, "info", "exclude");
|
|
15
20
|
const existingContent = await readOptionalFile(excludePath);
|
|
16
21
|
const existingEntries = new Set(existingContent
|
|
17
22
|
.split(/\r?\n/)
|
|
18
23
|
.map((line) => line.trim())
|
|
19
24
|
.filter((line) => line.length > 0 && !line.startsWith("#")));
|
|
20
|
-
|
|
25
|
+
const missingEntries = ignoreEntries.filter((ignoreEntry) => !existingEntries.has(ignoreEntry));
|
|
26
|
+
if (missingEntries.length === 0) {
|
|
21
27
|
return;
|
|
22
28
|
}
|
|
23
29
|
await mkdir(path.dirname(excludePath), { recursive: true });
|
|
24
30
|
const nextContent = existingContent.length === 0
|
|
25
|
-
? `${
|
|
26
|
-
: `${existingContent}${existingContent.endsWith("\n") ? "" : "\n"}${
|
|
31
|
+
? `${missingEntries.join("\n")}\n`
|
|
32
|
+
: `${existingContent}${existingContent.endsWith("\n") ? "" : "\n"}${missingEntries.join("\n")}\n`;
|
|
27
33
|
await writeFile(excludePath, nextContent, "utf8");
|
|
28
34
|
}
|
|
35
|
+
async function assertSafeLocalSettingsTarget(targetPath, repoRoot) {
|
|
36
|
+
const pathsToInspect = repoRoot
|
|
37
|
+
? getPathsFromRepoRoot(repoRoot, targetPath)
|
|
38
|
+
: [path.dirname(targetPath), targetPath];
|
|
39
|
+
for (const currentPath of pathsToInspect) {
|
|
40
|
+
await assertPathIsNotSymlink(currentPath, getSymlinkErrorMessage(currentPath, targetPath, repoRoot));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function assertPathIsNotSymlink(filePath, errorMessage) {
|
|
44
|
+
try {
|
|
45
|
+
const fileStats = await lstat(filePath);
|
|
46
|
+
if (fileStats.isSymbolicLink()) {
|
|
47
|
+
throw new Error(errorMessage);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (isMissingFileError(error)) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function getPathsFromRepoRoot(repoRoot, targetPath) {
|
|
58
|
+
const relativeTargetPath = requireRepoRelativePath(targetPath, repoRoot);
|
|
59
|
+
const targetSegments = relativeTargetPath.split(path.sep).filter((segment) => segment.length > 0);
|
|
60
|
+
const paths = [repoRoot];
|
|
61
|
+
let currentPath = repoRoot;
|
|
62
|
+
for (const segment of targetSegments) {
|
|
63
|
+
currentPath = path.join(currentPath, segment);
|
|
64
|
+
paths.push(currentPath);
|
|
65
|
+
}
|
|
66
|
+
return paths;
|
|
67
|
+
}
|
|
68
|
+
function requireRepoRelativePath(targetPath, repoRoot) {
|
|
69
|
+
const relativeTargetPath = path.relative(repoRoot, targetPath);
|
|
70
|
+
if (relativeTargetPath.length === 0 || relativeTargetPath.startsWith("..") || path.isAbsolute(relativeTargetPath)) {
|
|
71
|
+
throw new Error("Expected local Claude Code settings to stay inside the current git repository.");
|
|
72
|
+
}
|
|
73
|
+
return relativeTargetPath;
|
|
74
|
+
}
|
|
75
|
+
function getSymlinkErrorMessage(currentPath, targetPath, repoRoot) {
|
|
76
|
+
if (currentPath === path.dirname(targetPath)) {
|
|
77
|
+
return 'Refusing to write local Claude Code settings into a symlinked ".claude" directory.';
|
|
78
|
+
}
|
|
79
|
+
if (currentPath === targetPath) {
|
|
80
|
+
return "Refusing to overwrite local Claude Code settings through a symlinked settings file.";
|
|
81
|
+
}
|
|
82
|
+
const label = repoRoot ? path.relative(repoRoot, currentPath) || path.basename(currentPath) : currentPath;
|
|
83
|
+
return `Refusing local Claude Code setup through a symlinked path component: ${label}.`;
|
|
84
|
+
}
|
|
85
|
+
async function assertTargetIsNotTracked(relativeTargetPath, repoRoot) {
|
|
86
|
+
try {
|
|
87
|
+
await execFileAsync("git", ["-C", repoRoot, "ls-files", "--error-unmatch", "--", relativeTargetPath], {
|
|
88
|
+
encoding: "utf8"
|
|
89
|
+
});
|
|
90
|
+
throw new Error(`Refusing local install because ${relativeTargetPath} is already tracked by git. Remove it from the index before writing secrets.`);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
if (isMissingGitBinaryError(error)) {
|
|
94
|
+
throw new Error("Git is required to verify that local Claude Code settings are not already tracked before writing secrets.");
|
|
95
|
+
}
|
|
96
|
+
if (isGitPathUnmatchedError(error)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
29
102
|
async function findGitContext(startDirectory) {
|
|
30
103
|
let currentDirectory = path.resolve(startDirectory);
|
|
31
104
|
for (;;) {
|
|
@@ -73,4 +146,13 @@ async function readOptionalFile(filePath) {
|
|
|
73
146
|
return "";
|
|
74
147
|
}
|
|
75
148
|
}
|
|
149
|
+
function isMissingFileError(error) {
|
|
150
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
151
|
+
}
|
|
152
|
+
function isMissingGitBinaryError(error) {
|
|
153
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
154
|
+
}
|
|
155
|
+
function isGitPathUnmatchedError(error) {
|
|
156
|
+
return error instanceof Error && "code" in error && error.code === 1;
|
|
157
|
+
}
|
|
76
158
|
//# sourceMappingURL=local-git-ignore.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-git-ignore.js","sourceRoot":"","sources":["../../src/install/local-git-ignore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"local-git-ignore.js","sourceRoot":"","sources":["../../src/install/local-git-ignore.ts"],"names":[],"mappings":"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"}
|
package/docs/how-it-works.md
CHANGED
|
@@ -29,7 +29,9 @@ The installer writes one Claude Code settings file:
|
|
|
29
29
|
|
|
30
30
|
It does not touch any shell profile or other configuration file.
|
|
31
31
|
|
|
32
|
-
For `local` scope inside a git repository, it also adds `.claude/settings.local.json` to `.git/info/exclude` before writing the file.
|
|
32
|
+
For `local` scope inside a git repository, it also adds `.claude/settings.local.json` and `.claude/settings.local.json.backup-*` to `.git/info/exclude` before writing the file.
|
|
33
|
+
|
|
34
|
+
For safety, local scope refuses to proceed if `.claude/settings.local.json` is already tracked by git, or if any existing path component on the way to the target is a symlink. That includes a symlinked `.claude` directory and a symlinked local settings file.
|
|
33
35
|
|
|
34
36
|
## Write behavior
|
|
35
37
|
|
|
@@ -37,7 +39,7 @@ When the target file already exists, the installer:
|
|
|
37
39
|
|
|
38
40
|
1. Parses the existing JSON
|
|
39
41
|
2. Refuses to continue if the JSON is invalid
|
|
40
|
-
3. Creates a timestamped backup
|
|
42
|
+
3. Creates a timestamped backup with owner-only permissions
|
|
41
43
|
4. Preserves unrelated settings
|
|
42
44
|
5. Updates only the relevant `env` keys, including all Claude model env vars with the selected curated model id
|
|
43
45
|
6. Adds Claude Code's JSON schema if `$schema` is missing
|
package/docs/security.md
CHANGED
|
@@ -17,14 +17,18 @@ Claude Code reads environment variables from its settings files. That means the
|
|
|
17
17
|
- `~/.claude/settings.json`
|
|
18
18
|
- `.claude/settings.local.json`
|
|
19
19
|
|
|
20
|
-
This repo does not write `.env` files and does not modify shell startup files. It also writes the target settings file with owner-only permissions.
|
|
20
|
+
This repo does not write `.env` files and does not modify shell startup files. It also writes the target settings file with owner-only permissions and locks backup files down to owner-only permissions too.
|
|
21
21
|
|
|
22
22
|
## Local scope hygiene
|
|
23
23
|
|
|
24
|
-
If you choose `local` scope inside a git repository, the installer
|
|
24
|
+
If you choose `local` scope inside a git repository, the installer first verifies that `.claude/settings.local.json` is not already tracked by git. If it is already tracked, the installer stops instead of writing a secret into a tracked file.
|
|
25
|
+
|
|
26
|
+
For untracked local installs, the installer adds `.claude/settings.local.json` and its timestamped backup pattern to the repo's `.git/info/exclude` before writing the file so secret-bearing local settings do not immediately show up in git.
|
|
25
27
|
|
|
26
28
|
If your team already manages ignores another way, that is still fine. The important rule is that `.claude/settings.local.json` must stay uncommitted.
|
|
27
29
|
|
|
30
|
+
The installer also refuses local setup through a symlinked path component on the way to `.claude/settings.local.json`, including a symlinked `.claude` directory or a symlinked local settings file. That prevents a repository from redirecting the secret-bearing write to some other tracked or non-local location.
|
|
31
|
+
|
|
28
32
|
## Backup behavior
|
|
29
33
|
|
|
30
34
|
Before overwriting an existing target settings file, the installer creates a timestamped backup next to it. If something looks wrong afterward, restore from that backup and rerun the installer.
|
package/docs/troubleshooting.md
CHANGED
|
@@ -51,6 +51,22 @@ Restore from the timestamped backup next to the settings file, or fix the JSON m
|
|
|
51
51
|
|
|
52
52
|
Rerun the installer and choose `local` scope. That writes `.claude/settings.local.json` in the current repository instead of editing your global `~/.claude/settings.json`.
|
|
53
53
|
|
|
54
|
+
## Local install refused because `.claude/settings.local.json` is already tracked by git
|
|
55
|
+
|
|
56
|
+
For `local` scope, the installer now stops if `.claude/settings.local.json` is already a tracked file in the repository.
|
|
57
|
+
|
|
58
|
+
That safety check prevents the installer from writing your API key into a file that `git status` would continue to treat as commit-ready.
|
|
59
|
+
|
|
60
|
+
Remove the file from the git index first, or use `user` scope instead, then rerun the installer.
|
|
61
|
+
|
|
62
|
+
## Local install refused because the target path uses a symlink
|
|
63
|
+
|
|
64
|
+
For `local` scope, the installer now rejects a symlinked path component anywhere on the way to `.claude/settings.local.json`, including a symlinked `.claude` directory or a symlinked `.claude/settings.local.json`.
|
|
65
|
+
|
|
66
|
+
That safety check prevents a repository from redirecting your API key into some other location that git might track.
|
|
67
|
+
|
|
68
|
+
Replace the symlink with a real `.claude` directory, then rerun the installer.
|
|
69
|
+
|
|
54
70
|
## Manual settings edits
|
|
55
71
|
|
|
56
72
|
Manual edits to the Claude Code settings file are possible, but they are outside the supported public install flow for this repo.
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gonkagate/claude-code",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
5
|
-
"homepage": "https://github.com/GonkaGate/gonkagate-claude-code",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "CLI installer for using Claude Code with GonkaGate on Gonka Network.",
|
|
5
|
+
"homepage": "https://github.com/GonkaGate/gonkagate-claude-code#readme",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/GonkaGate/gonkagate-claude-code/issues"
|
|
8
8
|
},
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"type": "module",
|
|
14
14
|
"bin": {
|
|
15
|
-
"gonkagate-claude-code": "
|
|
15
|
+
"gonkagate-claude-code": "bin/gonkagate-claude-code.js"
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"bin",
|
|
@@ -27,18 +27,22 @@
|
|
|
27
27
|
"build": "tsc -p tsconfig.json",
|
|
28
28
|
"dev": "tsx src/cli.ts",
|
|
29
29
|
"prepack": "npm run build",
|
|
30
|
-
"test": "
|
|
30
|
+
"test": "node scripts/run-tests.mjs",
|
|
31
31
|
"ci": "npm test && npm run build"
|
|
32
32
|
},
|
|
33
33
|
"engines": {
|
|
34
34
|
"node": ">=18"
|
|
35
35
|
},
|
|
36
36
|
"keywords": [
|
|
37
|
-
"claude-code",
|
|
38
37
|
"gonkagate",
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
38
|
+
"gonka",
|
|
39
|
+
"gonka ai",
|
|
40
|
+
"gonka-network",
|
|
41
|
+
"gonka blockchain",
|
|
42
|
+
"gonka-api",
|
|
43
|
+
"gonka gateway",
|
|
44
|
+
"claude-code",
|
|
45
|
+
"claude code"
|
|
42
46
|
],
|
|
43
47
|
"license": "Apache-2.0",
|
|
44
48
|
"packageManager": "npm@11.11.1",
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { readdirSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const repoRoot = resolve(scriptDir, '..');
|
|
8
|
+
const testRoot = join(repoRoot, 'test');
|
|
9
|
+
|
|
10
|
+
function collectTestFiles(directory) {
|
|
11
|
+
const entries = readdirSync(directory, { withFileTypes: true }).sort((a, b) =>
|
|
12
|
+
a.name.localeCompare(b.name),
|
|
13
|
+
);
|
|
14
|
+
const files = [];
|
|
15
|
+
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
const fullPath = join(directory, entry.name);
|
|
18
|
+
|
|
19
|
+
if (entry.isDirectory()) {
|
|
20
|
+
files.push(...collectTestFiles(fullPath));
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (entry.isFile() && entry.name.endsWith('.test.ts')) {
|
|
25
|
+
files.push(fullPath);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return files;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const testFiles = collectTestFiles(testRoot);
|
|
33
|
+
|
|
34
|
+
if (testFiles.length === 0) {
|
|
35
|
+
console.error('No test files found under test/.');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const tsxCliPath = join(repoRoot, 'node_modules', 'tsx', 'dist', 'cli.mjs');
|
|
40
|
+
const result = spawnSync(process.execPath, [tsxCliPath, '--test', ...testFiles], {
|
|
41
|
+
cwd: repoRoot,
|
|
42
|
+
stdio: 'inherit',
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (result.error) {
|
|
46
|
+
throw result.error;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
process.exit(result.status ?? 1);
|