Skip to content

Commit 7958b5e

Browse files
cnbailianclaude
andauthored
fix: resolve npm version publishing and auth token discovery issues (#21)
* fix: resolve npm version publishing and auth token discovery issues Fix .npmrc path resolution for bun publish --cwd by writing to ~/.npmrc instead of project root, ensuring token is discoverable regardless of working directory. Add plugin_version and repterm_api_version workflow inputs to enable manual version management for plugin-api and plugin-kubectl packages. Improve semantic-release workspace protocol handling to process all package.json files consistently. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add missing fumadocs-core dependency to docs app The docs app imports from fumadocs-core/source but only had fumadocs-ui and fumadocs-mdx as direct dependencies, causing webpack module resolution failure in CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 92aab8c commit 7958b5e

50 files changed

Lines changed: 3780 additions & 11 deletions

Some content is hidden

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

.github/workflows/build.yml

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,19 @@ on:
1515
required: true
1616
type: boolean
1717
default: false
18+
plugin_version:
19+
description: "Plugin version to publish (e.g. 0.2.0)"
20+
required: false
21+
type: string
1822
release_repterm_api:
1923
description: "Release repterm-api"
2024
required: true
2125
type: boolean
2226
default: false
27+
repterm_api_version:
28+
description: "repterm-api version to publish (e.g. 0.2.0)"
29+
required: false
30+
type: string
2331

2432
permissions:
2533
contents: write
@@ -115,7 +123,10 @@ jobs:
115123
- name: Resolve workspace protocol for npm compatibility
116124
run: |
117125
API_VERSION=$(jq -r .version packages/plugin-api/package.json)
118-
sed -i "s/\"workspace:\*\"/\"^${API_VERSION}\"/g" packages/repterm/package.json
126+
# Replace workspace:* in all package.json files (npm cannot parse bun's workspace protocol)
127+
find packages -name package.json -not -path "*/node_modules/*" -exec sed -i "s/\"workspace:\*\"/\"^${API_VERSION}\"/g" {} +
128+
# Remove workspaces field from root package.json so npm doesn't try to process the workspace graph
129+
jq 'del(.workspaces)' package.json > package.json.tmp && mv package.json.tmp package.json
119130
120131
- name: Setup Node.js
121132
uses: actions/setup-node@v4
@@ -232,10 +243,14 @@ jobs:
232243
run: bun install --frozen-lockfile
233244

234245
- name: Configure npm auth
235-
run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc
246+
run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
236247
env:
237248
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
238249

250+
- name: Set plugin version
251+
if: inputs.plugin_version != ''
252+
run: cd packages/plugin-kubectl && npm version ${{ inputs.plugin_version }} --no-git-tag-version
253+
239254
- name: Build repterm-api package
240255
run: bun run build:plugin-api
241256

@@ -263,10 +278,14 @@ jobs:
263278
run: bun install --frozen-lockfile
264279

265280
- name: Configure npm auth
266-
run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc
281+
run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc
267282
env:
268283
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
269284

285+
- name: Set repterm-api version
286+
if: inputs.repterm_api_version != ''
287+
run: cd packages/plugin-api && npm version ${{ inputs.repterm_api_version }} --no-git-tag-version
288+
270289
- name: Build repterm-api package
271290
run: bun run build:plugin-api
272291

apps/docs/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Fumadocs auto-generated
2+
.source/
3+
4+
# Next.js
5+
.next/
6+
out/
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { source } from '@/lib/source';
2+
import {
3+
DocsPage,
4+
DocsBody,
5+
DocsDescription,
6+
DocsTitle,
7+
} from 'fumadocs-ui/page';
8+
import { notFound } from 'next/navigation';
9+
import defaultMdxComponents from 'fumadocs-ui/mdx';
10+
11+
export default async function Page(props: {
12+
params: Promise<{ slug?: string[] }>;
13+
}) {
14+
const params = await props.params;
15+
const page = source.getPage(params.slug);
16+
if (!page) notFound();
17+
18+
const MDX = page.data.body;
19+
20+
return (
21+
<DocsPage toc={page.data.toc} full={page.data.full}>
22+
<DocsTitle>{page.data.title}</DocsTitle>
23+
<DocsDescription>{page.data.description}</DocsDescription>
24+
<DocsBody>
25+
<MDX components={{ ...defaultMdxComponents }} />
26+
</DocsBody>
27+
</DocsPage>
28+
);
29+
}
30+
31+
export async function generateStaticParams() {
32+
return source.generateParams();
33+
}
34+
35+
export async function generateMetadata(props: {
36+
params: Promise<{ slug?: string[] }>;
37+
}) {
38+
const params = await props.params;
39+
const page = source.getPage(params.slug);
40+
if (!page) notFound();
41+
42+
return {
43+
title: page.data.title,
44+
description: page.data.description,
45+
};
46+
}

apps/docs/app/docs/layout.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { source } from '@/lib/source';
2+
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
3+
import type { ReactNode } from 'react';
4+
import { baseOptions } from '@/app/layout.config';
5+
6+
export default function Layout({ children }: { children: ReactNode }) {
7+
return (
8+
<DocsLayout tree={source.pageTree} {...baseOptions}>
9+
{children}
10+
</DocsLayout>
11+
);
12+
}

apps/docs/app/global.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@import 'tailwindcss';
2+
@import 'fumadocs-ui/css/neutral.css';
3+
@import 'fumadocs-ui/css/preset.css';

apps/docs/app/layout.config.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
2+
3+
export const baseOptions: BaseLayoutProps = {
4+
nav: {
5+
title: 'Repterm',
6+
},
7+
links: [
8+
{
9+
text: 'Documentation',
10+
url: '/docs',
11+
active: 'nested-url',
12+
},
13+
],
14+
};

apps/docs/app/layout.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { RootProvider } from 'fumadocs-ui/provider';
2+
import type { ReactNode } from 'react';
3+
import './global.css';
4+
5+
export default function RootLayout({ children }: { children: ReactNode }) {
6+
return (
7+
<html lang="en" suppressHydrationWarning>
8+
<body>
9+
<RootProvider>{children}</RootProvider>
10+
</body>
11+
</html>
12+
);
13+
}

apps/docs/app/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { redirect } from 'next/navigation';
2+
3+
export default function HomePage() {
4+
redirect('/docs');
5+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
title: Assertions
3+
description: Reference for all built-in assertion matchers.
4+
---
5+
6+
## CommandResult Assertions
7+
8+
Use `expect(result)` where `result` is a `CommandResult` from `$` or `terminal.run()`.
9+
10+
```ts
11+
const result = await $`echo hello`;
12+
13+
// Success/failure
14+
await expect(result).toSucceed(); // code === 0
15+
await expect(result).toFail(); // code !== 0
16+
await expect(result).toHaveExitCode(0); // specific exit code
17+
18+
// Output matching
19+
await expect(result).toContainInOutput('hello'); // check combined output
20+
await expect(result).toHaveStdout('hello'); // partial stdout match
21+
await expect(result).toHaveStderr('error'); // partial stderr match
22+
23+
// Regex matching
24+
await expect(result).toMatchStdout(/ready/i); // regex on stdout
25+
await expect(result).toMatchStderr(/error/i); // regex on stderr
26+
```
27+
28+
### Assertion Reference
29+
30+
| Matcher | Description |
31+
| --- | --- |
32+
| `toSucceed()` | Assert exit code is 0 |
33+
| `toFail()` | Assert exit code is non-zero |
34+
| `toHaveExitCode(code)` | Assert specific exit code |
35+
| `toContainInOutput(text)` | Assert combined stdout+stderr contains text |
36+
| `toHaveStdout(text)` | Assert stdout contains text |
37+
| `toHaveStderr(text)` | Assert stderr contains text |
38+
| `toMatchStdout(regex)` | Assert stdout matches regex pattern |
39+
| `toMatchStderr(regex)` | Assert stderr matches regex pattern |
40+
41+
## Terminal Assertions
42+
43+
Use `expect(terminal)` to assert on the current terminal state:
44+
45+
```ts
46+
await expect(terminal).toContainText('prompt'); // text in terminal
47+
await expect(terminal).toMatchPattern(/\$\s/); // regex on terminal
48+
```
49+
50+
| Matcher | Description |
51+
| --- | --- |
52+
| `toContainText(text)` | Assert terminal output contains text |
53+
| `toMatchPattern(regex)` | Assert terminal output matches regex pattern |
54+
55+
## Tips
56+
57+
- In **Spawn mode**, exit code assertions (`toSucceed`, `toFail`, `toHaveExitCode`) work reliably.
58+
- In **PTY/Recording mode**, exit code is often `-1`. Prefer output assertions (`toContainInOutput`, `toHaveStdout`).
59+
- Use `$({ silent: true })` to get reliable exit codes in any mode.
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
---
2+
title: Command Options
3+
description: Full reference for RunOptions — configure timeout, environment, prompt detection, and more.
4+
---
5+
6+
## RunOptions
7+
8+
All command execution methods (`$`, `terminal.run()`) accept an options object:
9+
10+
```ts
11+
interface RunOptions {
12+
timeout?: number;
13+
env?: Record<string, string>;
14+
cwd?: string;
15+
interactive?: boolean;
16+
silent?: boolean;
17+
typingSpeed?: number;
18+
pauseAfter?: number;
19+
pauseBefore?: number;
20+
promptDetection?: 'auto' | 'osc133' | 'sentinel' | 'regex' | 'none';
21+
}
22+
```
23+
24+
## Option Reference
25+
26+
### timeout
27+
28+
Command timeout in milliseconds. If the command doesn't complete within this time, it is killed.
29+
30+
- **Default**: `300000` (5 minutes)
31+
- **Example**: `$({ timeout: 60_000 })\`long-running-cmd\``
32+
33+
### env
34+
35+
Additional environment variables for the command. Merged with the current environment.
36+
37+
```ts
38+
await $({ env: { NODE_ENV: 'test', DEBUG: '1' } })`node app.js`;
39+
```
40+
41+
### cwd
42+
43+
Working directory for the command.
44+
45+
```ts
46+
await $({ cwd: '/tmp/my-project' })`npm test`;
47+
```
48+
49+
### interactive
50+
51+
Enable PTY mode with `expect`/`send` control for interactive commands.
52+
53+
- **Default**: `false`
54+
- **Example**: `$({ interactive: true })\`python3\``
55+
56+
When enabled, the returned `PTYProcess` provides `expect()`, `send()`, `sendRaw()`, `interrupt()`, and `wait()` methods.
57+
58+
### silent
59+
60+
Force `Bun.spawn` even in PTY or recording mode. Use this when you need:
61+
- Reliable exit codes
62+
- Clean JSON output (no terminal escape sequences)
63+
- Separate stdout/stderr streams
64+
65+
- **Default**: `false`
66+
- **Example**: `$({ silent: true })\`kubectl get pod -o json\``
67+
68+
### typingSpeed
69+
70+
Controls character typing speed during recording mode. Each character is typed with this delay between keystrokes.
71+
72+
- **Default**: `80` (ms per character)
73+
- **`0`**: Instant typing (no animation)
74+
- **Example**: `$({ typingSpeed: 40 })\`echo "fast typing"\``
75+
76+
Only affects recording mode. Ignored in other modes.
77+
78+
### pauseAfter
79+
80+
Pause after the command completes during recording mode. Useful for letting the viewer see the output before the next command.
81+
82+
- **Default**: none
83+
- **Example**: `$({ pauseAfter: 2000 })\`echo "wait for it..."\``
84+
85+
Only affects recording mode.
86+
87+
### pauseBefore
88+
89+
Pause before the command starts during recording mode.
90+
91+
- **Default**: none
92+
- **Example**: `$({ pauseBefore: 1000 })\`echo "dramatic pause"\``
93+
94+
Only affects recording mode.
95+
96+
### promptDetection
97+
98+
Override prompt detection strategy for this command.
99+
100+
| Value | Description |
101+
| --- | --- |
102+
| `'auto'` | Best available method (default) |
103+
| `'osc133'` | Force OSC 133 detection only |
104+
| `'sentinel'` | Force sentinel marker detection |
105+
| `'regex'` | Force regex-based detection |
106+
| `'none'` | Skip prompt detection entirely |
107+
108+
Use `'none'` for long-running or streaming commands where prompt detection is not applicable:
109+
110+
```ts
111+
await $({ promptDetection: 'none' })`tail -f /var/log/syslog`;
112+
```
113+
114+
## Usage Examples
115+
116+
```ts
117+
// Simple command with timeout
118+
await $({ timeout: 5000 })`echo hello`;
119+
120+
// Silent mode for reliable JSON parsing
121+
const result = await $({ silent: true })`kubectl get pods -o json`;
122+
const pods = JSON.parse(result.stdout);
123+
124+
// Interactive command
125+
const proc = $({ interactive: true, timeout: 30_000 })`python3`;
126+
await proc.expect('>>>');
127+
await proc.send('exit()\n');
128+
129+
// Recording with typing animation
130+
await $({ typingSpeed: 40, pauseAfter: 1000 })`ls -la`;
131+
132+
// Custom environment and working directory
133+
await $({ env: { PORT: '3000' }, cwd: '/app' })`npm start`;
134+
```

0 commit comments

Comments
 (0)