graphjin 3.11.3 → 3.13.0

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/README.md CHANGED
@@ -61,6 +61,7 @@ GraphJin started
61
61
  Web UI: http://localhost:8080/
62
62
  GraphQL: http://localhost:8080/api/v1/graphql
63
63
  REST API: http://localhost:8080/api/v1/rest/
64
+ Workflows: http://localhost:8080/api/v1/workflows/<name>
64
65
  MCP: http://localhost:8080/api/v1/mcp
65
66
 
66
67
  Claude Desktop Configuration
@@ -89,7 +90,7 @@ Copy the JSON config shown and add it to your Claude Desktop config file (see be
89
90
  GraphJin includes a guided installer that configures MCP for OpenAI Codex, Claude Code, or both.
90
91
 
91
92
  ```bash
92
- # Guided mode (asks target client, scope, and mode)
93
+ # Guided mode (asks target client and scope)
93
94
  graphjin mcp install
94
95
  ```
95
96
 
@@ -109,15 +110,12 @@ graphjin mcp install --client codex --scope project --yes
109
110
  graphjin mcp install --client claude --scope project --yes
110
111
  ```
111
112
 
112
- Backwards compatibility alias:
113
-
114
- ```bash
115
- graphjin mcp plugin install
116
- ```
117
-
118
113
  #### Troubleshooting
119
114
 
120
- - `graphjin mcp install` uses your `--path` value for stdio mode (`graphjin mcp --path <config-path>`).
115
+ - `graphjin mcp install` defaults to `--server http://localhost:8080/`.
116
+ - Set a custom server URL with `--server`, for example:
117
+ - `graphjin mcp install --client codex --server http://my-host:8080/ --yes`
118
+ - Claude installs use `graphjin mcp --server <url>` under the hood.
121
119
  - If Codex CLI does not support `codex mcp add --scope` (older versions), GraphJin automatically falls back to updating:
122
120
  - global scope: `~/.codex/config.toml`
123
121
  - local scope: `.codex/config.toml`
@@ -155,7 +153,8 @@ Copy paste the Claude Desktop Config provided by `graphjin serve` into the Claud
155
153
  1. **Connects to database** - Reads your schema automatically
156
154
  2. **Discovers relationships** - Foreign keys become navigable joins
157
155
  3. **Exposes MCP tools** - Teach any LLM the query syntax
158
- 4. **Compiles to SQL** - Every request becomes a single optimized query
156
+ 4. **Runs JS workflows** - Chain multiple GraphJin MCP tools in one reusable workflow
157
+ 5. **Compiles to SQL** - Every request becomes a single optimized query
159
158
 
160
159
  No resolvers. No ORM. No N+1 queries. Just point and query.
161
160
 
@@ -221,7 +220,45 @@ Works from Node.js, Go, or any WebSocket client.
221
220
 
222
221
  ## MCP Tools
223
222
 
224
- GraphJin exposes several tools that guide AI models to write valid queries. Key tools: `list_tables` and `describe_table` for schema discovery, `get_query_syntax` for learning the DSL, `execute_graphql` for running queries, and `execute_saved_query` for production-approved queries. Prompts like `write_query` and `fix_query_error` help models construct and debug queries.
223
+ GraphJin exposes several tools that guide AI models to write valid queries. Key tools: `list_tables` and `describe_table` for schema discovery, `get_query_syntax` for learning the DSL, `execute_graphql` for running queries, and `execute_saved_query` for production-approved queries.
224
+
225
+ For JS orchestration, use:
226
+ - `get_js_runtime_api` to discover exactly which globals/functions are available inside workflow scripts
227
+ - `execute_workflow` to run `./workflows/<name>.js` with input variables
228
+
229
+ Prompts like `write_query` and `fix_query_error` help models construct and debug queries.
230
+
231
+ ## JS Workflows (MCP + REST)
232
+
233
+ Workflows let an LLM run multi-step logic in JavaScript while still using GraphJin MCP tools for DB-aware operations.
234
+
235
+ Create a file in `./workflows`, for example `./workflows/customer_insights.js`:
236
+
237
+ ```js
238
+ function main(input) {
239
+ const tables = gj.tools.listTables({});
240
+ const top = gj.tools.executeSavedQuery({
241
+ name: "top_customers",
242
+ variables: { limit: input.limit || 5 }
243
+ });
244
+ return { tables, top };
245
+ }
246
+ ```
247
+
248
+ ### Run via MCP
249
+
250
+ Call:
251
+ - `get_js_runtime_api` first (for exact runtime schema)
252
+ - `execute_workflow` with:
253
+ - `name`: workflow file name (with or without `.js`)
254
+ - `variables`: input payload passed to global `input` and `main(input)`
255
+
256
+ ### Run via REST
257
+
258
+ - `POST /api/v1/workflows/<name>` with JSON body
259
+ - `GET /api/v1/workflows/<name>?variables={...json...}`
260
+
261
+ Both map variables to the same workflow input object.
225
262
 
226
263
  ## Chat Walkthroughs
227
264
 
package/bin/graphjin.js CHANGED
@@ -1,10 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  const { spawn } = require('child_process');
3
3
  const path = require('path');
4
+ const fs = require('fs');
4
5
 
5
6
  const ext = process.platform === 'win32' ? '.exe' : '';
6
7
  const binary = path.join(__dirname, 'graphjin' + ext);
7
8
 
9
+ if (!fs.existsSync(binary)) {
10
+ console.error(`GraphJin binary not found at: ${binary}`);
11
+ console.error('Try reinstalling: npm install -g graphjin');
12
+ process.exit(1);
13
+ }
14
+
8
15
  const child = spawn(binary, process.argv.slice(2), { stdio: 'inherit' });
9
16
 
10
17
  child.on('error', (err) => {
@@ -13,4 +20,10 @@ child.on('error', (err) => {
13
20
  process.exit(1);
14
21
  });
15
22
 
16
- child.on('exit', (code) => process.exit(code || 0));
23
+ child.on('exit', (code, signal) => {
24
+ if (signal) {
25
+ console.error(`graphjin terminated by signal: ${signal}`);
26
+ process.exit(1);
27
+ }
28
+ process.exit(code ?? 0);
29
+ });
package/bin/install.js CHANGED
@@ -39,6 +39,29 @@ async function download(url, dest) {
39
39
  });
40
40
  }
41
41
 
42
+ async function downloadWithFallback(candidates, binDir) {
43
+ let lastErr;
44
+
45
+ for (const candidate of candidates) {
46
+ const archivePath = path.join(binDir, candidate.filename);
47
+
48
+ try {
49
+ await download(candidate.url, archivePath);
50
+ return {
51
+ archivePath,
52
+ filename: candidate.filename,
53
+ };
54
+ } catch (err) {
55
+ lastErr = err;
56
+ try {
57
+ fs.unlinkSync(archivePath);
58
+ } catch {}
59
+ }
60
+ }
61
+
62
+ throw lastErr || new Error('No download candidates available');
63
+ }
64
+
42
65
  async function extract(tarPath, destDir) {
43
66
  // Use tar module for extraction
44
67
  const tar = require('tar');
@@ -62,14 +85,28 @@ async function install() {
62
85
  process.exit(1);
63
86
  }
64
87
 
65
- const ext = platform === 'windows' ? '.zip' : '.tar.gz';
66
- const filename = `graphjin_${platform}_${arch}${ext}`;
67
- const url = `https://github.com/dosco/graphjin/releases/download/v${version}/${filename}`;
68
-
69
88
  const binDir = __dirname;
70
- const archivePath = path.join(binDir, filename);
71
89
  const binaryName = platform === 'windows' ? 'graphjin.exe' : 'graphjin';
72
90
  const binaryPath = path.join(binDir, binaryName);
91
+ const releaseBase = `https://github.com/dosco/graphjin/releases/download/v${version}`;
92
+
93
+ // New releases include version in the archive name. Keep legacy fallback
94
+ // names to remain backward compatible with older tags.
95
+ const filenames = platform === 'windows'
96
+ ? [
97
+ `graphjin_${version}_${platform}_${arch}.tar.gz`,
98
+ `graphjin_${version}_${platform}_${arch}.zip`,
99
+ `graphjin_${platform}_${arch}.tar.gz`,
100
+ `graphjin_${platform}_${arch}.zip`,
101
+ ]
102
+ : [
103
+ `graphjin_${version}_${platform}_${arch}.tar.gz`,
104
+ `graphjin_${platform}_${arch}.tar.gz`,
105
+ ];
106
+ const candidates = filenames.map((filename) => ({
107
+ filename,
108
+ url: `${releaseBase}/${filename}`,
109
+ }));
73
110
 
74
111
  // Skip if binary already exists
75
112
  if (fs.existsSync(binaryPath)) {
@@ -79,12 +116,17 @@ async function install() {
79
116
 
80
117
  console.log(`Downloading GraphJin v${version} for ${platform}/${arch}...`);
81
118
 
119
+ let archivePath = '';
120
+ let selectedFilename = '';
121
+
82
122
  try {
83
- await download(url, archivePath);
123
+ const selected = await downloadWithFallback(candidates, binDir);
124
+ archivePath = selected.archivePath;
125
+ selectedFilename = selected.filename;
84
126
 
85
127
  console.log('Extracting...');
86
128
 
87
- if (platform === 'windows') {
129
+ if (platform === 'windows' && selectedFilename.endsWith('.zip')) {
88
130
  // For Windows, use PowerShell to extract
89
131
  execSync(`powershell -command "Expand-Archive -Path '${archivePath}' -DestinationPath '${binDir}' -Force"`, {
90
132
  stdio: 'inherit',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphjin",
3
- "version": "3.11.3",
3
+ "version": "3.13.0",
4
4
  "description": "GraphJin CLI - Build APIs in 5 minutes with GraphQL",
5
5
  "bin": {
6
6
  "graphjin": "bin/graphjin.js"