vibebox 0.0.0 → 0.0.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/Dockerfile +86 -0
- package/LICENSE.md +187 -0
- package/README.md +155 -2
- package/container-scripts/port-monitor.sh +37 -0
- package/container-scripts/startup.sh +109 -0
- package/container-scripts/watcher.sh +46 -0
- package/dist/agents/auth.js +77 -0
- package/dist/agents/claude/index.js +176 -0
- package/dist/agents/index.js +24 -0
- package/dist/agents/types.js +2 -0
- package/dist/auth.js +11 -0
- package/dist/index.js +883 -0
- package/dist/update.js +256 -0
- package/package.json +25 -24
- package/.dockerignore +0 -13
- package/LICENSE +0 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCredentialStore = getCredentialStore;
|
|
4
|
+
exports.readCredentialsFile = readCredentialsFile;
|
|
5
|
+
const node_child_process_1 = require("node:child_process");
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
const node_os_1 = require("node:os");
|
|
8
|
+
function getCredentialStore() {
|
|
9
|
+
switch ((0, node_os_1.platform)()) {
|
|
10
|
+
case "darwin":
|
|
11
|
+
return {
|
|
12
|
+
get: (service, account) => {
|
|
13
|
+
try {
|
|
14
|
+
return (0, node_child_process_1.execSync)(`security find-generic-password -a "${account}" -s "${service}" -w`, { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
set: (service, account, value) => {
|
|
21
|
+
const hexData = Buffer.from(value, "utf-8").toString("hex");
|
|
22
|
+
const input = `add-generic-password -U -a "${account}" -s "${service}" -X "${hexData}"\n`;
|
|
23
|
+
(0, node_child_process_1.execSync)("security -i", {
|
|
24
|
+
input,
|
|
25
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
case "win32":
|
|
30
|
+
// Use cmdkey (built-in) instead of CredentialManager module
|
|
31
|
+
return {
|
|
32
|
+
get: (service, account) => {
|
|
33
|
+
try {
|
|
34
|
+
const target = `${service}:${account}`.replace(/"/g, '""');
|
|
35
|
+
// cmdkey /list doesn't expose password, use PowerShell with Windows Vault API
|
|
36
|
+
const ps = `[Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR((Get-StoredCredential -Target '${target}' -AsCredentialObject).Password))`;
|
|
37
|
+
return (0, node_child_process_1.execSync)(`powershell -Command "${ps.replace(/"/g, '\\"')}"`, { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim() || null;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
set: (service, account, value) => {
|
|
44
|
+
const target = `${service}:${account}`.replace(/"/g, '""');
|
|
45
|
+
// Use base64 to avoid shell escaping issues with credentials
|
|
46
|
+
const b64 = Buffer.from(value, "utf8").toString("base64");
|
|
47
|
+
const ps = `$cred = [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${b64}')); cmdkey /generic:"${target}" /user:"${account.replace(/"/g, '""')}" /pass:"$cred"`;
|
|
48
|
+
(0, node_child_process_1.execSync)(`powershell -Command "${ps.replace(/"/g, '\\"')}"`, { stdio: ["pipe", "pipe", "pipe"] });
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
default:
|
|
52
|
+
// Linux: secret-tool (libsecret)
|
|
53
|
+
return {
|
|
54
|
+
get: (service, account) => {
|
|
55
|
+
try {
|
|
56
|
+
return (0, node_child_process_1.execSync)(`secret-tool lookup service "${service}" account "${account}"`, { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
set: (service, account, value) => {
|
|
63
|
+
(0, node_child_process_1.execSync)(`echo -n "${value}" | secret-tool store --label="${service}" service "${service}" account "${account}"`);
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function readCredentialsFile(path) {
|
|
69
|
+
try {
|
|
70
|
+
if (!(0, node_fs_1.existsSync)(path))
|
|
71
|
+
return null;
|
|
72
|
+
return JSON.parse((0, node_fs_1.readFileSync)(path, "utf8"));
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.claude = void 0;
|
|
4
|
+
exports.getCredentialPaths = getCredentialPaths;
|
|
5
|
+
const auth_1 = require("../auth");
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_os_1 = require("node:os");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const node_crypto_1 = require("node:crypto");
|
|
11
|
+
const KEYCHAIN_SERVICES = [
|
|
12
|
+
"Claude Code-credentials",
|
|
13
|
+
"claude-code-credentials",
|
|
14
|
+
"claude.ai",
|
|
15
|
+
];
|
|
16
|
+
function getKeychainServiceName() {
|
|
17
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR;
|
|
18
|
+
if (configDir) {
|
|
19
|
+
const hash = (0, node_crypto_1.createHash)("sha256")
|
|
20
|
+
.update(configDir)
|
|
21
|
+
.digest("hex")
|
|
22
|
+
.substring(0, 8);
|
|
23
|
+
return `Claude Code-credentials-${hash}`;
|
|
24
|
+
}
|
|
25
|
+
return "Claude Code-credentials";
|
|
26
|
+
}
|
|
27
|
+
function extractFromKeychain() {
|
|
28
|
+
const store = (0, auth_1.getCredentialStore)();
|
|
29
|
+
const user = (0, node_os_1.userInfo)().username;
|
|
30
|
+
for (const svc of KEYCHAIN_SERVICES) {
|
|
31
|
+
const data = store.get(svc, user);
|
|
32
|
+
if (data) {
|
|
33
|
+
try {
|
|
34
|
+
const creds = JSON.parse(data);
|
|
35
|
+
if (creds.claudeAiOauth?.accessToken)
|
|
36
|
+
return creds;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
function compareFreshness(first, second) {
|
|
46
|
+
const firstExpiry = first?.claudeAiOauth?.expiresAt;
|
|
47
|
+
const secondExpiry = second?.claudeAiOauth?.expiresAt;
|
|
48
|
+
if (!first?.claudeAiOauth?.accessToken && !second?.claudeAiOauth?.accessToken) {
|
|
49
|
+
return "unknown";
|
|
50
|
+
}
|
|
51
|
+
if (!first?.claudeAiOauth?.accessToken)
|
|
52
|
+
return "second";
|
|
53
|
+
if (!second?.claudeAiOauth?.accessToken)
|
|
54
|
+
return "first";
|
|
55
|
+
if (firstExpiry === undefined && secondExpiry === undefined)
|
|
56
|
+
return "equal";
|
|
57
|
+
if (firstExpiry === undefined)
|
|
58
|
+
return "second";
|
|
59
|
+
if (secondExpiry === undefined)
|
|
60
|
+
return "first";
|
|
61
|
+
if (firstExpiry > secondExpiry)
|
|
62
|
+
return "first";
|
|
63
|
+
if (secondExpiry > firstExpiry)
|
|
64
|
+
return "second";
|
|
65
|
+
return "equal";
|
|
66
|
+
}
|
|
67
|
+
exports.claude = {
|
|
68
|
+
name: "claude",
|
|
69
|
+
command: "claude",
|
|
70
|
+
configDir: (0, node_path_1.join)((0, node_os_1.homedir)(), ".claude"),
|
|
71
|
+
isInstalledOnHost: () => {
|
|
72
|
+
try {
|
|
73
|
+
(0, node_child_process_1.execSync)("claude --version", { stdio: "pipe" });
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
dockerArgs: ({ workspace, home, containerOnly }) => {
|
|
81
|
+
const claudeDir = (0, node_path_1.join)(home, ".claude");
|
|
82
|
+
const configCopy = (0, node_path_1.join)(workspace, ".vibebox", "claude.json");
|
|
83
|
+
const config = (0, node_path_1.join)(home, ".claude.json");
|
|
84
|
+
// Container-only mode: minimal mounts, container manages its own ~/.claude
|
|
85
|
+
if (containerOnly) {
|
|
86
|
+
return [
|
|
87
|
+
// Mount config copy (not the original, to prevent corruption)
|
|
88
|
+
"-v", `${configCopy}:${config}`,
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
// Host mode: mount ~/.claude with selective hiding for isolation
|
|
92
|
+
const projectFolder = workspace.replace(/[\\/]/g, "-");
|
|
93
|
+
const emptyDir = (0, node_path_1.join)(workspace, ".vibebox", "empty");
|
|
94
|
+
const emptyFile = (0, node_path_1.join)(workspace, ".vibebox", "empty-file");
|
|
95
|
+
const credentials = (0, node_path_1.join)(claudeDir, ".credentials.json");
|
|
96
|
+
return [
|
|
97
|
+
// Mount ~/.claude with selective hiding
|
|
98
|
+
"-v", `${claudeDir}:${claudeDir}`,
|
|
99
|
+
"-v", `${emptyDir}:${claudeDir}/projects`,
|
|
100
|
+
"-v", `${emptyFile}:${claudeDir}/history.jsonl`,
|
|
101
|
+
"-v", `${claudeDir}/projects/${projectFolder}:${claudeDir}/projects/${projectFolder}`,
|
|
102
|
+
// Mount credentials
|
|
103
|
+
"-v", `${credentials}:${credentials}`,
|
|
104
|
+
// Mount config copy (not the original, to prevent corruption)
|
|
105
|
+
"-v", `${configCopy}:${config}`,
|
|
106
|
+
];
|
|
107
|
+
},
|
|
108
|
+
install: (containerName) => {
|
|
109
|
+
console.log("Installing Claude Code...");
|
|
110
|
+
(0, node_child_process_1.spawnSync)("docker", ["exec", containerName, "bash", "-c", "curl -fsSL https://claude.ai/install.sh | sh"], { stdio: "inherit" });
|
|
111
|
+
},
|
|
112
|
+
setup: ({ workspace, containerOnly }) => {
|
|
113
|
+
const home = (0, node_os_1.homedir)();
|
|
114
|
+
const claudeDir = (0, node_path_1.join)(home, ".claude");
|
|
115
|
+
const credFile = (0, node_path_1.join)(claudeDir, ".credentials.json");
|
|
116
|
+
// Ensure directories exist
|
|
117
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.join)(workspace, ".vibebox", "empty"), { recursive: true });
|
|
118
|
+
// Create empty overlay file
|
|
119
|
+
const emptyFile = (0, node_path_1.join)(workspace, ".vibebox", "empty-file");
|
|
120
|
+
if (!(0, node_fs_1.existsSync)(emptyFile))
|
|
121
|
+
(0, node_fs_1.writeFileSync)(emptyFile, "");
|
|
122
|
+
// Create minimal config to skip onboarding and trust prompts
|
|
123
|
+
const configCopy = (0, node_path_1.join)(workspace, ".vibebox", "claude.json");
|
|
124
|
+
const minimalConfig = {
|
|
125
|
+
hasCompletedOnboarding: true,
|
|
126
|
+
projects: {
|
|
127
|
+
[workspace]: {
|
|
128
|
+
allowedTools: [],
|
|
129
|
+
hasTrustDialogAccepted: true,
|
|
130
|
+
hasCompletedProjectOnboarding: true,
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
(0, node_fs_1.writeFileSync)(configCopy, JSON.stringify(minimalConfig, null, 2));
|
|
135
|
+
// Container-only mode: skip credential sync, agent will prompt for login
|
|
136
|
+
if (containerOnly)
|
|
137
|
+
return;
|
|
138
|
+
// Host mode: sync credentials from system credential store
|
|
139
|
+
(0, node_fs_1.mkdirSync)(claudeDir, { recursive: true });
|
|
140
|
+
// Clean up if credentials file is a directory (Claude bug)
|
|
141
|
+
if ((0, node_fs_1.existsSync)(credFile) && (0, node_fs_1.lstatSync)(credFile).isDirectory()) {
|
|
142
|
+
(0, node_fs_1.rmSync)(credFile, { recursive: true, force: true });
|
|
143
|
+
}
|
|
144
|
+
// Sync credentials from system credential store (macOS only for now)
|
|
145
|
+
if (process.platform === "darwin") {
|
|
146
|
+
const keychainCreds = extractFromKeychain();
|
|
147
|
+
const fileCreds = (0, auth_1.readCredentialsFile)(credFile);
|
|
148
|
+
const freshness = compareFreshness(keychainCreds, fileCreds);
|
|
149
|
+
if (freshness === "first" || freshness === "equal") {
|
|
150
|
+
if (!keychainCreds)
|
|
151
|
+
throw new Error("No credentials. Run: claude auth login");
|
|
152
|
+
(0, node_fs_1.writeFileSync)(credFile, JSON.stringify(keychainCreds, null, 2));
|
|
153
|
+
}
|
|
154
|
+
else if (freshness === "second" && fileCreds) {
|
|
155
|
+
// File is fresher, sync back to keychain
|
|
156
|
+
const store = (0, auth_1.getCredentialStore)();
|
|
157
|
+
store.set(getKeychainServiceName(), (0, node_os_1.userInfo)().username, JSON.stringify(fileCreds));
|
|
158
|
+
}
|
|
159
|
+
else if (!keychainCreds && !fileCreds) {
|
|
160
|
+
throw new Error("No credentials. Run: claude auth login");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
if (!(0, node_fs_1.existsSync)(credFile)) {
|
|
165
|
+
throw new Error("No credentials. Run: claude auth login");
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
versionCommand: "claude --version",
|
|
170
|
+
};
|
|
171
|
+
function getCredentialPaths() {
|
|
172
|
+
return {
|
|
173
|
+
credentials: (0, node_path_1.join)((0, node_os_1.homedir)(), ".claude", ".credentials.json"),
|
|
174
|
+
config: (0, node_path_1.join)((0, node_os_1.homedir)(), ".claude.json"),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.agents = void 0;
|
|
4
|
+
exports.detectInstalledAgents = detectInstalledAgents;
|
|
5
|
+
exports.isAgentInstalled = isAgentInstalled;
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
|
+
const claude_1 = require("./claude");
|
|
8
|
+
exports.agents = {
|
|
9
|
+
claude: claude_1.claude,
|
|
10
|
+
};
|
|
11
|
+
function detectInstalledAgents() {
|
|
12
|
+
return Object.entries(exports.agents)
|
|
13
|
+
.filter(([_, agent]) => agent.isInstalledOnHost())
|
|
14
|
+
.map(([name]) => name);
|
|
15
|
+
}
|
|
16
|
+
function isAgentInstalled(containerName, agent) {
|
|
17
|
+
try {
|
|
18
|
+
(0, node_child_process_1.execSync)(`docker exec ${containerName} which ${agent.command}`, { stdio: "pipe" });
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCredentialPaths = void 0;
|
|
4
|
+
exports.ensureCredentials = ensureCredentials;
|
|
5
|
+
// Re-export from Claude agent for backward compatibility
|
|
6
|
+
const claude_1 = require("./agents/claude");
|
|
7
|
+
Object.defineProperty(exports, "getCredentialPaths", { enumerable: true, get: function () { return claude_1.getCredentialPaths; } });
|
|
8
|
+
function ensureCredentials() {
|
|
9
|
+
// Delegates to Claude agent's setup with current working directory
|
|
10
|
+
claude_1.claude.setup?.({ workspace: process.cwd() });
|
|
11
|
+
}
|