Skip to content

Commit ea038be

Browse files
committed
✨ feat(cli): harden auth, TLS, retries, and repo parsing
remove token-in-URL pushes; use GIT_ASKPASS add TLS config (caFile/insecure) + config doctor add retry/backoff and safer API errors centralize origin parsing and reduce process.exit in helpers
1 parent 5711f08 commit ea038be

63 files changed

Lines changed: 5228 additions & 1364 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 92 additions & 20 deletions
Large diffs are not rendered by default.

cli/README.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
<p align="center">
2-
<img src="https://raw.githubusercontent.com/swadhinbiswas/OpenCodeHub/main/public/logo.svg" alt="OpenCodeHub CLI" width="120" />
2+
<picture>
3+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/swadhinbiswas/OpenCodeHub/main/public/logo-dark.png">
4+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/swadhinbiswas/OpenCodeHub/main/public/logo-light.png">
5+
<img src="https://raw.githubusercontent.com/swadhinbiswas/OpenCodeHub/main/public/logo-light.png" alt="OpenCodeHub CLI" width="400" />
6+
</picture>
37
</p>
48

59
<h1 align="center">OpenCodeHub CLI</h1>
@@ -107,6 +111,7 @@ och repo list
107111
## 🎨 Beautiful Output Examples
108112

109113
### Push Command
114+
110115
```
111116
ℹ Pushing to swadhinbiswas/myrepo
112117
Branch: master
@@ -122,7 +127,7 @@ och repo list
122127
✔ Uploaded 49.17 KB in 0.52s (94.56 KB/s)
123128
124129
remote: Processing: 100% (90/90), done.
125-
remote:
130+
remote:
126131
To https://opencodehub.com/swadhinbiswas/myrepo.git
127132
abc1234..def5678 master -> master
128133
@@ -140,6 +145,7 @@ To https://opencodehub.com/swadhinbiswas/myrepo.git
140145
```
141146

142147
### Clone Command
148+
143149
```
144150
ℹ Cloning swadhinbiswas/awesome-project
145151
@@ -164,14 +170,15 @@ Receiving objects: 100% (234/234), 1.23 MiB | 2.45 MiB/s, done.
164170
```
165171

166172
### Create Command
173+
167174
```
168175
ℹ Creating 🌐 my-new-repo
169176
Description: An awesome new project
170177
171178
✔ Repository created
172179
173180
✨ SUCCESS! ✨
174-
181+
175182
Repository swadhinbiswas/my-new-repo is ready!
176183
177184
╭─────────────────────────────────────────╮
@@ -204,15 +211,15 @@ jobs:
204211
runs-on: ubuntu-latest
205212
steps:
206213
- uses: actions/checkout@v4
207-
214+
208215
- name: Setup Node
209216
uses: actions/setup-node@v4
210217
with:
211-
node-version: '20'
212-
218+
node-version: "20"
219+
213220
- name: Install CLI
214221
run: npm install -g opencodehub-cli
215-
222+
216223
- name: Push to OpenCodeHub
217224
run: |
218225
och auth login --token ${{ secrets.OCH_TOKEN }}
@@ -247,6 +254,7 @@ och config set defaultBranch main
247254
## 📦 What's New in v1.1.0
248255

249256
✨ **Production-Grade UI Overhaul**
257+
250258
- GitHub-like progress indicators
251259
- Beautiful ASCII art and gradients
252260
- Real-time upload/download speeds

cli/bin/och.ts

Lines changed: 71 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ import { stackCommands } from "../src/commands/stack/index.js";
3838
const git = simpleGit();
3939
const program = new Command();
4040

41+
if (!process.stdout.isTTY) {
42+
chalk.level = 0;
43+
}
44+
4145
// Custom help formatter with Dracula theme
4246
program.configureHelp({
4347
sortSubcommands: true,
@@ -60,33 +64,79 @@ program.addHelpText("beforeAll", () => {
6064
); // Dracula comment gray
6165
});
6266

67+
function isHiddenCommand(cmd: Command) {
68+
return Boolean((cmd as { hidden?: boolean }).hidden);
69+
}
70+
71+
function buildCommandTable() {
72+
const commands = program.commands
73+
.filter((cmd) => !isHiddenCommand(cmd))
74+
.map((cmd) => ({
75+
name: cmd.name(),
76+
desc: cmd.description() || "",
77+
}))
78+
.sort((a, b) => a.name.localeCompare(b.name));
79+
80+
const maxDescWidth = 46;
81+
const nameWidth = Math.max(
82+
"Command".length,
83+
...commands.map((c) => c.name.length),
84+
);
85+
const descWidth = Math.min(
86+
maxDescWidth,
87+
Math.max("Description".length, ...commands.map((c) => c.desc.length)),
88+
);
89+
90+
const top = ` ┌${"─".repeat(nameWidth + 2)}${"─".repeat(descWidth + 2)}┐`;
91+
const header =
92+
` │ ${chalk.hex("#50fa7b").bold("Command".padEnd(nameWidth))} ` +
93+
`│ ${chalk.hex("#50fa7b").bold("Description".padEnd(descWidth))} │`;
94+
const mid = ` ├${"─".repeat(nameWidth + 2)}${"─".repeat(descWidth + 2)}┤`;
95+
96+
const rows = commands.map(({ name, desc }) => {
97+
const clipped =
98+
desc.length > descWidth ? `${desc.slice(0, descWidth - 1)}…` : desc;
99+
return (
100+
` │ ${chalk.hex("#ff79c6")(name.padEnd(nameWidth))} ` +
101+
`│ ${chalk.hex("#f8f8f2")(clipped.padEnd(descWidth))} │`
102+
);
103+
});
104+
105+
const bottom = ` └${"─".repeat(nameWidth + 2)}${"─".repeat(descWidth + 2)}┘`;
106+
107+
return [top, header, mid, ...rows, bottom].join("\n");
108+
}
109+
63110
// Customize command help colors with Dracula theme
64111
const originalHelp = program.helpInformation.bind(program);
65112
program.helpInformation = function () {
66113
const help = originalHelp();
114+
const commandsIndex = help.indexOf("Commands:");
115+
const commandsBlock =
116+
commandsIndex >= 0 ? help.slice(0, commandsIndex) : help;
117+
118+
const styled = commandsBlock
119+
// Usage line - Dracula cyan
120+
.replace(
121+
/^Usage: (.+)$/gm,
122+
(_, usage) =>
123+
chalk.hex("#8be9fd").bold("Usage: ") + chalk.hex("#f8f8f2")(usage),
124+
)
125+
// Section headers - Dracula green
126+
.replace(/^Options:$/gm, "\n" + chalk.hex("#50fa7b").bold("Options:"))
127+
// Flags - Dracula purple
128+
.replace(/(-[a-zA-Z-]+)/g, chalk.hex("#bd93f9")("$1"))
129+
// Arguments - Dracula yellow
130+
.replace(/<([^>]+)>/g, chalk.hex("#f1fa8c")("<$1>"))
131+
// Optional params - Dracula comment
132+
.replace(/\[([^\]]+)\]/g, chalk.hex("#6272a4")("[$1]"));
67133

68134
return (
69-
help
70-
// Usage line - Dracula cyan
71-
.replace(
72-
/^Usage: (.+)$/gm,
73-
(_, usage) =>
74-
chalk.hex("#8be9fd").bold("Usage: ") + chalk.hex("#f8f8f2")(usage),
75-
)
76-
// Section headers - Dracula green
77-
.replace(/^Options:$/gm, "\n" + chalk.hex("#50fa7b").bold("Options:"))
78-
.replace(/^Commands:$/gm, "\n" + chalk.hex("#50fa7b").bold("Commands:"))
79-
// Flags - Dracula purple
80-
.replace(/(-[a-zA-Z-]+)/g, chalk.hex("#bd93f9")("$1"))
81-
// Arguments - Dracula yellow
82-
.replace(/<([^>]+)>/g, chalk.hex("#f1fa8c")("<$1>"))
83-
// Optional params - Dracula comment
84-
.replace(/\[([^\]]+)\]/g, chalk.hex("#6272a4")("[$1]"))
85-
// Command names at start of lines - Dracula pink
86-
.replace(
87-
/^(\s+)([a-z-]+)(\s)/gm,
88-
(match, space, cmd, after) => space + chalk.hex("#ff79c6")(cmd) + after,
89-
)
135+
styled +
136+
"\n" +
137+
chalk.hex("#50fa7b").bold("\nCommands:") +
138+
"\n" +
139+
buildCommandTable()
90140
);
91141
};
92142

cli/src/commands/config/index.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ configCommands
9696
const value = config.get(key as keyof OchConfig);
9797

9898
if (key === "token") {
99-
if (value) {
99+
if (typeof value === "string" && value) {
100100
console.log(value.slice(0, 12) + "...");
101101
} else {
102102
console.log(chalk.dim("(not set)"));
@@ -139,11 +139,11 @@ configCommands
139139
key === "insecure" ? value === "true" || value === "1" : value;
140140

141141
config.set(key as keyof OchConfig, normalizedValue as any);
142-
console.log(
143-
chalk.green(
144-
`✓ Set ${key} = ${key === "token" ? value.slice(0, 12) + "..." : value}`,
145-
),
146-
);
142+
const displayValue =
143+
key === "token" && typeof normalizedValue === "string"
144+
? `${normalizedValue.slice(0, 12)}...`
145+
: String(normalizedValue);
146+
console.log(chalk.green(`✓ Set ${key} = ${displayValue}`));
147147
});
148148

149149
// Config Unset

docs-site/astro.config.mjs

Lines changed: 106 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,111 @@
11
// @ts-check
2-
import { defineConfig } from 'astro/config';
3-
import starlight from '@astrojs/starlight';
2+
import starlight from "@astrojs/starlight";
3+
import { defineConfig } from "astro/config";
44

55
// https://astro.build/config
66
export default defineConfig({
7-
integrations: [
8-
starlight({
9-
title: 'OpenCodeHub Docs',
10-
social: [
11-
{ icon: 'github', label: 'GitHub', href: 'https://github.com/swadhinbiswas/OpencodeHub' },
12-
],
13-
sidebar: [
14-
{
15-
label: 'Getting Started',
16-
items: [
17-
{ label: 'Installation', slug: 'getting-started/installation' },
18-
{ label: 'Quick Start', slug: 'getting-started/quick-start' },
19-
{ label: 'First Repository', slug: 'getting-started/first-repository' },
20-
],
21-
},
22-
{
23-
label: 'Guides',
24-
items: [
25-
{ label: 'Team Workflows', slug: 'guides/team-workflows' },
26-
{ label: 'Branch Protection', slug: 'guides/branch-protection' },
27-
{ label: 'Webhooks', slug: 'guides/webhooks' },
28-
{ label: 'Storage Adapters', slug: 'guides/storage-adapters' },
29-
],
30-
},
31-
{
32-
label: 'Features',
33-
items: [
34-
{ label: 'Stacked PRs', slug: 'features/stacked-prs' },
35-
{ label: 'AI Review', slug: 'features/ai-review' },
36-
{ label: 'Merge Queue', slug: 'features/merge-queue' },
37-
],
38-
},
39-
{
40-
label: 'Tutorials',
41-
autogenerate: { directory: 'tutorials' },
42-
},
43-
{
44-
label: 'Administration',
45-
autogenerate: { directory: 'administration' },
46-
},
47-
{
48-
label: 'API Reference',
49-
autogenerate: { directory: 'api' },
50-
},
51-
{
52-
label: 'Development',
53-
autogenerate: { directory: 'development' },
54-
},
55-
{
56-
label: 'Reference',
57-
autogenerate: { directory: 'reference' },
58-
},
59-
],
60-
}),
61-
],
7+
integrations: [
8+
starlight({
9+
title: "OpenCodeHub Docs",
10+
logo: {
11+
light: "../../public/logo-light.png",
12+
dark: "../../public/logo-dark.png",
13+
replacesTitle: false,
14+
},
15+
social: [
16+
{
17+
icon: "github",
18+
label: "GitHub",
19+
href: "https://github.com/swadhinbiswas/OpencodeHub",
20+
},
21+
],
22+
sidebar: [
23+
{
24+
label: "Getting Started",
25+
items: [
26+
{ label: "Installation", slug: "getting-started/installation" },
27+
{ label: "Quick Start", slug: "getting-started/quick-start" },
28+
{
29+
label: "First Repository",
30+
slug: "getting-started/first-repository",
31+
},
32+
],
33+
},
34+
{
35+
label: "Guides",
36+
items: [
37+
{ label: "Team Workflows", slug: "guides/team-workflows" },
38+
{ label: "Branch Protection", slug: "guides/branch-protection" },
39+
{ label: "Webhooks", slug: "guides/webhooks" },
40+
{ label: "Storage Adapters", slug: "guides/storage-adapters" },
41+
],
42+
},
43+
{
44+
label: "CLI",
45+
items: [
46+
{ label: "CLI Overview", slug: "reference/cli-overview" },
47+
{ label: "Auth & Config", slug: "reference/cli-auth-config" },
48+
{ label: "Core Commands", slug: "reference/cli-core-commands" },
49+
{ label: "Stack Workflows", slug: "reference/cli-stack-workflows" },
50+
{ label: "Merge Queue", slug: "reference/cli-merge-queue" },
51+
{
52+
label: "Automation & Insights",
53+
slug: "reference/cli-automation-insights",
54+
},
55+
{ label: "CLI Command Reference", slug: "reference/cli-commands" },
56+
],
57+
},
58+
{
59+
label: "Features",
60+
items: [
61+
{ label: "Stacked PRs", slug: "features/stacked-prs" },
62+
{ label: "AI Review", slug: "features/ai-review" },
63+
{ label: "Merge Queue", slug: "features/merge-queue" },
64+
{ label: "Automation Rules", slug: "features/automations" },
65+
{ label: "PR Inbox", slug: "features/inbox" },
66+
{ label: "Developer Metrics", slug: "features/developer-metrics" },
67+
{ label: "Notifications", slug: "features/notifications" },
68+
{ label: "CI/CD Actions", slug: "features/ci-actions" },
69+
{ label: "CLI Workflows", slug: "features/cli" },
70+
],
71+
},
72+
{
73+
label: "Tutorials",
74+
autogenerate: { directory: "tutorials" },
75+
},
76+
{
77+
label: "Deployment",
78+
items: [
79+
{ label: "Docker", slug: "administration/deploy-docker" },
80+
{ label: "Podman", slug: "administration/deploy-podman" },
81+
{ label: "Nginx Proxy", slug: "administration/deploy-nginx" },
82+
{ label: "cPanel", slug: "administration/deploy-cpanel" },
83+
{ label: "CyberPanel", slug: "administration/deploy-cyberpanel" },
84+
{ label: "Cloudflare", slug: "administration/deploy-cloudflare" },
85+
],
86+
},
87+
{
88+
label: "Administration",
89+
items: [
90+
{ label: "Production Guide", slug: "administration/deployment" },
91+
{ label: "Configuration", slug: "administration/configuration" },
92+
{ label: "Monitoring", slug: "administration/monitoring" },
93+
{ label: "Security", slug: "administration/security" },
94+
],
95+
},
96+
{
97+
label: "API Reference",
98+
autogenerate: { directory: "api" },
99+
},
100+
{
101+
label: "Development",
102+
autogenerate: { directory: "development" },
103+
},
104+
{
105+
label: "Reference",
106+
autogenerate: { directory: "reference" },
107+
},
108+
],
109+
}),
110+
],
62111
});

docs-site/public/favicon.png

64 KB
Loading

0 commit comments

Comments
 (0)