Skip to content

Commit 1b9a23a

Browse files
authored
feat(client-ts): add TypeScript SDK and CI/CD workflows (#17)
* feat(client-ts): add TypeScript SDK and CI/CD workflows * feat(client-ts): refine TypeScript SDK with ESLint, type improvements, and workflow updates - Add ESLint configuration for TypeScript client - Update zod version and devDependencies - Refine TypeScript type assertions for node-fetch and form-data - Improve type casting for editor content updates - Update health check endpoint in workflows
1 parent cc500ae commit 1b9a23a

32 files changed

Lines changed: 8121 additions & 2 deletions
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
name: TypeScript Client Release
2+
3+
on:
4+
release:
5+
types: [published]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: read
10+
id-token: write
11+
12+
jobs:
13+
publish:
14+
name: Publish to npm
15+
runs-on: ubuntu-latest
16+
defaults:
17+
run:
18+
working-directory: src/client/acontext-ts
19+
steps:
20+
- name: Checkout repository
21+
uses: actions/checkout@v4
22+
23+
- name: Set up Node.js
24+
uses: actions/setup-node@v4
25+
with:
26+
node-version: '20'
27+
registry-url: 'https://registry.npmjs.org'
28+
cache: 'npm'
29+
cache-dependency-path: src/client/acontext-ts/package-lock.json
30+
env:
31+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
32+
33+
- name: Install dependencies
34+
run: |
35+
npm ci
36+
# Install peer dependencies for Node.js environment
37+
npm install --no-save node-fetch@^3.3.2 form-data@^4.0.0 || true
38+
39+
- name: Run lint
40+
run: npm run lint
41+
42+
- name: Build
43+
run: npm run build
44+
45+
- name: Start containers
46+
working-directory: src/server
47+
run: |
48+
cp .env.example .env
49+
cp core/config.yaml.example core/config.yaml
50+
51+
docker compose up acontext-server-pg acontext-server-redis acontext-server-rabbitmq acontext-server-seaweedfs-setup acontext-server-seaweedfs acontext-server-api -d
52+
53+
- name: Wait for API to be ready
54+
run: |
55+
timeout=120
56+
elapsed=0
57+
while [ $elapsed -lt $timeout ]; do
58+
if curl -f http://localhost:8029/health > /dev/null 2>&1; then
59+
echo "API is ready"
60+
exit 0
61+
fi
62+
echo "Waiting for API to be ready... ($elapsed/$timeout seconds)"
63+
sleep 5
64+
elapsed=$((elapsed + 5))
65+
done
66+
echo "API did not become ready in time"
67+
exit 1
68+
69+
- name: Run tests
70+
run: npm test
71+
env:
72+
CI: true
73+
ACONTEXT_API_KEY: sk-ac-your-root-api-bearer-token
74+
ACONTEXT_BASE_URL: http://localhost:8029/api/v1
75+
76+
- name: Extract version from package.json
77+
id: version
78+
run: |
79+
VERSION=$(node -p "require('./package.json').version")
80+
echo "version=$VERSION" >> $GITHUB_OUTPUT
81+
82+
- name: Check if version exists
83+
id: check_version
84+
run: |
85+
VERSION="${{ steps.version.outputs.version }}"
86+
if npm view acontext@$VERSION version > /dev/null 2>&1; then
87+
echo "exists=true" >> $GITHUB_OUTPUT
88+
echo "Version $VERSION already exists on npm"
89+
else
90+
echo "exists=false" >> $GITHUB_OUTPUT
91+
echo "Version $VERSION does not exist on npm"
92+
fi
93+
94+
- name: Publish to npm
95+
if: steps.check_version.outputs.exists == 'false'
96+
run: npm publish --access public
97+
98+
- name: Stop containers
99+
working-directory: src/server
100+
if: ${{ always() }}
101+
run: |
102+
docker compose down
103+
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: TypeScript Client Test
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- dev
8+
paths:
9+
- 'src/client/acontext-ts/**'
10+
- '.github/workflows/client-test-ts.yaml'
11+
pull_request:
12+
branches:
13+
- main
14+
- dev
15+
paths:
16+
- 'src/client/acontext-ts/**'
17+
- '.github/workflows/client-test-ts.yaml'
18+
19+
jobs:
20+
test:
21+
runs-on: ubuntu-latest
22+
permissions:
23+
contents: read
24+
steps:
25+
- uses: actions/checkout@v4
26+
27+
- name: Set up Node.js
28+
uses: actions/setup-node@v4
29+
with:
30+
node-version: '20'
31+
cache: 'npm'
32+
cache-dependency-path: src/client/acontext-ts/package-lock.json
33+
34+
- name: Install dependencies
35+
working-directory: src/client/acontext-ts
36+
run: |
37+
npm ci
38+
# Install peer dependencies for Node.js environment
39+
npm install --no-save node-fetch@^3.3.2 form-data@^4.0.0 || true
40+
41+
- name: Run lint
42+
working-directory: src/client/acontext-ts
43+
run: npm run lint
44+
45+
- name: Build
46+
working-directory: src/client/acontext-ts
47+
run: npm run build
48+
49+
- name: Start containers
50+
working-directory: src/server
51+
run: |
52+
cp .env.example .env
53+
cp core/config.yaml.example core/config.yaml
54+
55+
docker compose up acontext-server-pg acontext-server-redis acontext-server-rabbitmq acontext-server-seaweedfs-setup acontext-server-seaweedfs acontext-server-api -d
56+
57+
- name: Wait for API to be ready
58+
run: |
59+
timeout=120
60+
elapsed=0
61+
while [ $elapsed -lt $timeout ]; do
62+
if curl -f http://localhost:8029/health > /dev/null 2>&1; then
63+
echo "API is ready"
64+
exit 0
65+
fi
66+
echo "Waiting for API to be ready... ($elapsed/$timeout seconds)"
67+
sleep 5
68+
elapsed=$((elapsed + 5))
69+
done
70+
echo "API did not become ready in time"
71+
exit 1
72+
73+
- name: Run tests
74+
working-directory: src/client/acontext-ts
75+
run: npm test
76+
env:
77+
CI: true
78+
ACONTEXT_API_KEY: sk-ac-your-root-api-bearer-token
79+
ACONTEXT_BASE_URL: http://localhost:8029/api/v1
80+
81+
- name: Stop containers
82+
working-directory: src/server
83+
if: ${{ always() }}
84+
run: |
85+
docker compose down
86+

src/client/acontext-ts/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules/
2+
dist/
3+
*.log
4+
.DS_Store
5+
*.tsbuildinfo
6+

src/client/acontext-ts/.npmignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
src/
2+
tests/
3+
examples/
4+
tsconfig.json
5+
jest.config.js
6+
*.test.ts
7+
*.spec.ts
8+
.gitignore
9+

src/client/acontext-ts/README.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# acontext client for TypeScript
2+
3+
TypeScript SDK for interacting with the Acontext REST API.
4+
5+
## Installation
6+
7+
```bash
8+
npm install acontext
9+
```
10+
11+
## Quickstart
12+
13+
```typescript
14+
import { AcontextClient, MessagePart } from 'acontext';
15+
16+
const client = new AcontextClient({ apiKey: 'sk_project_token' });
17+
18+
// List spaces for the authenticated project
19+
const spaces = await client.spaces.list();
20+
21+
// Create a session bound to the first space
22+
const session = await client.sessions.create({ spaceId: spaces.items[0].id });
23+
24+
// Send a text message to the session
25+
await client.sessions.sendMessage(
26+
session.id,
27+
{
28+
role: 'user',
29+
parts: [MessagePart.textPart('Hello from TypeScript!')],
30+
},
31+
{ format: 'acontext' }
32+
);
33+
```
34+
35+
See the inline documentation for the full list of helpers covering sessions, spaces, disks, and artifact uploads.
36+
37+
## Managing disks and artifacts
38+
39+
Artifacts now live under project disks. Create a disk first, then upload files through the disk-scoped helper:
40+
41+
```typescript
42+
import { AcontextClient, FileUpload } from 'acontext';
43+
44+
const client = new AcontextClient({ apiKey: 'sk_project_token' });
45+
46+
const disk = await client.disks.create();
47+
await client.disks.artifacts.upsert(
48+
disk.id,
49+
{
50+
file: new FileUpload({
51+
filename: 'retro_notes.md',
52+
content: Buffer.from('# Retro Notes\nWe shipped file uploads successfully!\n'),
53+
contentType: 'text/markdown',
54+
}),
55+
filePath: '/notes/',
56+
meta: { source: 'readme-demo' },
57+
}
58+
);
59+
```
60+
61+
## Working with blocks
62+
63+
```typescript
64+
import { AcontextClient } from 'acontext';
65+
66+
const client = new AcontextClient({ apiKey: 'sk_project_token' });
67+
68+
const space = await client.spaces.create();
69+
const page = await client.blocks.create(space.id, {
70+
blockType: 'page',
71+
title: 'Kick-off Notes',
72+
});
73+
await client.blocks.create(space.id, {
74+
parentId: page.id,
75+
blockType: 'text',
76+
title: 'First block',
77+
props: { text: 'Plan the sprint goals' },
78+
});
79+
```
80+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import eslint from '@eslint/js';
2+
import tseslint from '@typescript-eslint/eslint-plugin';
3+
import tsparser from '@typescript-eslint/parser';
4+
import globals from 'globals';
5+
6+
export default [
7+
{
8+
ignores: ['node_modules/**', 'dist/**', 'tests/**'],
9+
},
10+
{
11+
files: ['src/**/*.ts'],
12+
languageOptions: {
13+
parser: tsparser,
14+
parserOptions: {
15+
ecmaVersion: 2020,
16+
sourceType: 'module',
17+
project: './tsconfig.json',
18+
},
19+
globals: {
20+
...globals.node,
21+
},
22+
},
23+
plugins: {
24+
'@typescript-eslint': tseslint,
25+
},
26+
rules: {
27+
...eslint.configs.recommended.rules,
28+
...tseslint.configs.recommended.rules,
29+
'@typescript-eslint/no-explicit-any': 'warn',
30+
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
31+
'no-undef': 'off', // TypeScript handles this
32+
},
33+
},
34+
];
35+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module.exports = {
2+
preset: 'ts-jest',
3+
testEnvironment: 'node',
4+
roots: ['<rootDir>/tests'],
5+
testMatch: ['**/*.test.ts'],
6+
transform: {
7+
'^.+\\.ts$': 'ts-jest',
8+
},
9+
collectCoverageFrom: ['src/**/*.ts', '!src/**/*.d.ts'],
10+
moduleNameMapper: {
11+
'^@/(.*)$': '<rootDir>/src/$1',
12+
},
13+
};
14+

0 commit comments

Comments
 (0)