@graphit/cli 0.1.0 → 0.1.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/package.json +1 -1
- package/skill/SKILL.md +70 -20
- package/skill/cursor/graphit-chart-patterns.mdc +67 -129
- package/skill/graphit.mdc +62 -80
- package/skill/references/chart-patterns.md +66 -128
- package/dist/api/client.d.ts +0 -16
- package/dist/api/client.js +0 -67
- package/dist/api/client.js.map +0 -1
- package/dist/auth/credentials.d.ts +0 -15
- package/dist/auth/credentials.js +0 -34
- package/dist/auth/credentials.js.map +0 -1
- package/dist/auth/login.d.ts +0 -2
- package/dist/auth/login.js +0 -229
- package/dist/auth/login.js.map +0 -1
- package/dist/auth/token.d.ts +0 -5
- package/dist/auth/token.js +0 -42
- package/dist/auth/token.js.map +0 -1
- package/dist/commands/auth.d.ts +0 -2
- package/dist/commands/auth.js +0 -68
- package/dist/commands/auth.js.map +0 -1
- package/dist/commands/connector.d.ts +0 -2
- package/dist/commands/connector.js +0 -97
- package/dist/commands/connector.js.map +0 -1
- package/dist/commands/dashboard.d.ts +0 -2
- package/dist/commands/dashboard.js +0 -124
- package/dist/commands/dashboard.js.map +0 -1
- package/dist/commands/ds.d.ts +0 -2
- package/dist/commands/ds.js +0 -53
- package/dist/commands/ds.js.map +0 -1
- package/dist/commands/kb.d.ts +0 -2
- package/dist/commands/kb.js +0 -259
- package/dist/commands/kb.js.map +0 -1
- package/dist/commands/query.d.ts +0 -2
- package/dist/commands/query.js +0 -61
- package/dist/commands/query.js.map +0 -1
- package/dist/commands/setup.d.ts +0 -2
- package/dist/commands/setup.js +0 -173
- package/dist/commands/setup.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -24
- package/dist/index.js.map +0 -1
- package/dist/output/format.d.ts +0 -8
- package/dist/output/format.js +0 -30
- package/dist/output/format.js.map +0 -1
- package/dist/output/json.d.ts +0 -1
- package/dist/output/json.js +0 -4
- package/dist/output/json.js.map +0 -1
- package/dist/output/table.d.ts +0 -2
- package/dist/output/table.js +0 -17
- package/dist/output/table.js.map +0 -1
|
@@ -1,142 +1,80 @@
|
|
|
1
1
|
# Chart Patterns
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
All chart types are available via `graphit.chart()`. All data comes from `graphit.resolve()` - never embed static data.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
```js
|
|
7
|
-
var maxVal = Math.max.apply(null, data.map(function(d){return d.value}));
|
|
8
|
-
data.forEach(function(d) {
|
|
9
|
-
var pct = (d.value / maxVal * 100).toFixed(1);
|
|
10
|
-
container.innerHTML += '<div style="display:flex;align-items:center;gap:12px;margin-bottom:8px">'
|
|
11
|
-
+ '<span style="width:100px;font-size:13px;font-weight:500;text-align:right">' + d.name + '</span>'
|
|
12
|
-
+ '<div style="flex:1;height:28px;background:var(--graphit-surface-sunken);border-radius:6px;overflow:hidden">'
|
|
13
|
-
+ '<div style="width:' + pct + '%;height:100%;background:var(--graphit-accent);border-radius:6px;min-width:2px"></div></div>'
|
|
14
|
-
+ '<span style="width:70px;font-size:13px;font-weight:600;text-align:right">' + fmt(d.value) + '</span></div>';
|
|
15
|
-
});
|
|
16
|
-
```
|
|
5
|
+
**NEVER use `<canvas>`.** Canvas produces blurry charts inside the sandboxed iframe due to DPI scaling issues.
|
|
17
6
|
|
|
18
|
-
##
|
|
19
|
-
```js
|
|
20
|
-
var maxVal = Math.max.apply(null, data.map(function(d){return d.value}));
|
|
21
|
-
container.style.cssText = 'display:flex;align-items:flex-end;gap:8px;height:200px;padding-top:20px';
|
|
22
|
-
data.forEach(function(d) {
|
|
23
|
-
var pct = (d.value / maxVal * 100).toFixed(1);
|
|
24
|
-
container.innerHTML += '<div style="flex:1;height:' + pct + '%;background:var(--graphit-accent);border-radius:4px 4px 0 0;'
|
|
25
|
-
+ 'position:relative;min-width:30px">'
|
|
26
|
-
+ '<span style="position:absolute;top:-20px;left:50%;transform:translateX(-50%);font-size:12px;font-weight:600">'
|
|
27
|
-
+ d.value + '</span>'
|
|
28
|
-
+ '<span style="position:absolute;bottom:-22px;left:50%;transform:translateX(-50%);font-size:11px;color:var(--graphit-fg-subtle);white-space:nowrap">'
|
|
29
|
-
+ d.name + '</span></div>';
|
|
30
|
-
});
|
|
31
|
-
```
|
|
7
|
+
## Runtime chart types
|
|
32
8
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
var colors = ['var(--graphit-accent)','#80CBC4','#26A69A','#5FA39B'];
|
|
36
|
-
data.forEach(function(row) {
|
|
37
|
-
var total = row.segments.reduce(function(a,s){return a+s.value},0);
|
|
38
|
-
var html = '<div style="display:flex;align-items:center;gap:12px;margin-bottom:8px">'
|
|
39
|
-
+ '<span style="width:100px;font-size:13px;font-weight:500;text-align:right">' + row.name + '</span>'
|
|
40
|
-
+ '<div style="flex:1;height:28px;display:flex;border-radius:6px;overflow:hidden">';
|
|
41
|
-
row.segments.forEach(function(s, i) {
|
|
42
|
-
html += '<div style="width:' + (s.value/total*100).toFixed(1) + '%;height:100%;background:' + colors[i%colors.length] + '"></div>';
|
|
43
|
-
});
|
|
44
|
-
html += '</div><span style="width:70px;font-size:13px;font-weight:600;text-align:right">' + total + '</span></div>';
|
|
45
|
-
container.innerHTML += html;
|
|
46
|
-
});
|
|
47
|
-
```
|
|
9
|
+
### bar
|
|
10
|
+
SVG vertical bars. Multi-series: grouped side-by-side. Y-axis: 4 grid lines with formatted labels. X-axis: category labels (sampled if >12). Rounded top corners (rx=3). `<title>` tooltips per bar.
|
|
48
11
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
var w = 600, h = 200, pad = 20;
|
|
52
|
-
var maxVal = Math.max.apply(null, data.map(function(d){return d.value}));
|
|
53
|
-
var points = data.map(function(d, i) {
|
|
54
|
-
var x = pad + (i / (data.length - 1)) * (w - 2*pad);
|
|
55
|
-
var y = h - pad - ((d.value / maxVal) * (h - 2*pad));
|
|
56
|
-
return x + ',' + y;
|
|
57
|
-
});
|
|
58
|
-
svg.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
|
|
59
|
-
svg.innerHTML =
|
|
60
|
-
'<path d="M' + points.join(' L') + ' L' + (w-pad) + ',' + (h-pad) + ' L' + pad + ',' + (h-pad) + ' Z" fill="rgba(77,182,172,0.15)"/>'
|
|
61
|
-
+ '<path d="M' + points.join(' L') + '" fill="none" stroke="var(--graphit-accent)" stroke-width="2"/>';
|
|
62
|
-
```
|
|
12
|
+
### line / area
|
|
13
|
+
SVG line with circle dots at each point. Area adds filled path below (opacity 0.18). Multi-series: separate colored paths. Y-axis scaled to data range (min to max). `<title>` tooltips per dot.
|
|
63
14
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
var colors = ['var(--graphit-accent)','#FF7043','#7E57C2','#42A5F5'];
|
|
67
|
-
series.forEach(function(s, si) {
|
|
68
|
-
var pts = s.values.map(function(v, i) {
|
|
69
|
-
var x = pad + (i / (s.values.length - 1)) * (w - 2*pad);
|
|
70
|
-
var y = h - pad - ((v / maxAll) * (h - 2*pad));
|
|
71
|
-
return x + ',' + y;
|
|
72
|
-
});
|
|
73
|
-
svg.innerHTML += '<path d="M' + pts.join(' L') + '" fill="none" stroke="' + colors[si] + '" stroke-width="2"/>';
|
|
74
|
-
});
|
|
75
|
-
```
|
|
15
|
+
### donut / pie
|
|
16
|
+
SVG pie slices with center hole (58% of radius). Center shows formatted total. Legend below with color swatches. Each slice is a wedge path from center.
|
|
76
17
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
var max = Math.max.apply(null,vals), min = Math.min.apply(null,vals);
|
|
81
|
-
var pts = vals.map(function(v,i){
|
|
82
|
-
return (i/(vals.length-1))*120 + ',' + (30 - ((v-min)/(max-min||1))*26 - 2);
|
|
83
|
-
}).join(' L');
|
|
84
|
-
sparkSvg.innerHTML = '<path d="M' + pts + '" fill="none" stroke="var(--graphit-accent)" stroke-width="1.5"/>';
|
|
85
|
-
```
|
|
18
|
+
### scatter / bubble
|
|
19
|
+
SVG circles positioned by x/y. Size field scales radius (sqrt, 3-40px). Fill opacity 0.6 with solid stroke. 4 horizontal grid lines. Capped at 200 points.
|
|
20
|
+
Config: `x`, `y`, `size` (optional radius), `label` (tooltip), `xFormat`.
|
|
86
21
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
var slices = [{v:40,c:'var(--graphit-accent)'},{v:25,c:'#80CBC4'},{v:20,c:'#26A69A'},{v:15,c:'#009688'}];
|
|
91
|
-
var total = slices.reduce(function(a,s){return a+s.v},0);
|
|
92
|
-
var cx=150, cy=150, r=120, hole=70, angle = -Math.PI/2;
|
|
93
|
-
slices.forEach(function(s) {
|
|
94
|
-
var arc = (s.v/total)*Math.PI*2;
|
|
95
|
-
ctx.beginPath(); ctx.moveTo(cx,cy);
|
|
96
|
-
ctx.arc(cx,cy,r,angle,angle+arc);
|
|
97
|
-
ctx.fillStyle=s.c; ctx.fill(); angle+=arc;
|
|
98
|
-
});
|
|
99
|
-
ctx.beginPath(); ctx.arc(cx,cy,hole,0,Math.PI*2);
|
|
100
|
-
ctx.fillStyle='white'; ctx.fill();
|
|
101
|
-
```
|
|
22
|
+
### stacked-bar / stacked
|
|
23
|
+
SVG vertical stacked bars. Segments stacked bottom-to-top per category. Y-axis scaled to max total. Legend shows series names.
|
|
24
|
+
Config: requires `series` field for stacking groups.
|
|
102
25
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
container.style.cssText = 'display:grid;grid-template-columns:repeat(' + cols + ',1fr);gap:2px';
|
|
107
|
-
cells.forEach(function(c) {
|
|
108
|
-
var intensity = c.value / max;
|
|
109
|
-
var bg = 'rgba(77,182,172,' + (0.1 + intensity*0.9).toFixed(2) + ')';
|
|
110
|
-
var fg = intensity > 0.6 ? 'white' : 'var(--graphit-fg)';
|
|
111
|
-
container.innerHTML += '<div style="aspect-ratio:1;border-radius:3px;display:flex;align-items:center;'
|
|
112
|
-
+ 'justify-content:center;font-size:11px;font-weight:600;background:' + bg + ';color:' + fg + '">'
|
|
113
|
-
+ c.value + '</div>';
|
|
114
|
-
});
|
|
115
|
-
```
|
|
26
|
+
### heatmap
|
|
27
|
+
CSS grid. X = columns, Y = rows. Background intensity scales linearly (rgba teal, 0.08-0.98). Text flips white at >55% intensity. Headers auto-generated. Capped at 40x40.
|
|
28
|
+
Config: `x` (column), `y` (row), `value` (intensity, falls back to `y`).
|
|
116
29
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
30
|
+
### funnel
|
|
31
|
+
CSS horizontal bars narrowing top-to-bottom. First bar = 100% width, rest proportional. Shows step-over-step conversion rate. Colors cycle through palette.
|
|
32
|
+
|
|
33
|
+
### gauge
|
|
34
|
+
CSS half-circle via `conic-gradient` + `clip-path`. Auto-colors: green (>=70%), yellow (>=40%), red (<40%). Override with `color` config (must be a valid CSS color token).
|
|
35
|
+
Config: `value`, `min` (default 0), `max` (default 100), `format`, `label`, `color`.
|
|
36
|
+
|
|
37
|
+
### sparkline
|
|
38
|
+
Inline SVG polyline for KPI cards or table cells. No axes, no grid - just trend shape. Single data point renders as a dot.
|
|
39
|
+
Config: `y`, `width` (default 120), `height` (default 32), `label`, `showValue` (default true), `valueFormat`.
|
|
40
|
+
|
|
41
|
+
## Color tokens
|
|
42
|
+
|
|
43
|
+
| Token | Usage |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `var(--graphit-accent)` | Primary brand teal |
|
|
46
|
+
| `var(--graphit-success)` | Positive/good |
|
|
47
|
+
| `var(--graphit-warning)` | Caution |
|
|
48
|
+
| `var(--graphit-error)` | Negative/bad |
|
|
49
|
+
| `var(--graphit-fg)` | Primary text |
|
|
50
|
+
| `var(--graphit-fg-muted)` | Secondary text |
|
|
51
|
+
| `var(--graphit-fg-subtle)` | Labels, placeholders |
|
|
52
|
+
| `var(--graphit-border)` | Borders, grid lines |
|
|
53
|
+
| `var(--graphit-surface-raised)` | Card backgrounds |
|
|
54
|
+
| `var(--graphit-surface-sunken)` | Inset areas |
|
|
55
|
+
|
|
56
|
+
## Tooltip pattern (for hand-rolled charts)
|
|
57
|
+
|
|
58
|
+
Runtime charts use `<title>` elements for native browser tooltips. For hand-rolled charts, add ONE shared tooltip div and always escape user data with a manual HTML escape function before passing to `showTooltip()`:
|
|
131
59
|
|
|
132
|
-
## CSS Gauge / Progress
|
|
133
60
|
```html
|
|
134
|
-
<div style="position:
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
61
|
+
<div id="tooltip" style="position:fixed;pointer-events:none;z-index:100;
|
|
62
|
+
background:var(--graphit-surface-raised);color:var(--graphit-fg);
|
|
63
|
+
border:1px solid var(--graphit-border);border-radius:8px;
|
|
64
|
+
padding:10px 14px;font-size:13px;line-height:1.5;
|
|
65
|
+
box-shadow:0 4px 12px rgba(0,0,0,0.15);opacity:0;transition:opacity 0.15s;
|
|
66
|
+
max-width:240px"></div>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
var tooltip = document.getElementById('tooltip');
|
|
71
|
+
function showTooltip(e, html) {
|
|
72
|
+
tooltip.innerHTML = html;
|
|
73
|
+
tooltip.style.opacity = '1';
|
|
74
|
+
var tx = Math.min(e.clientX + 12, window.innerWidth - 260);
|
|
75
|
+
var ty = e.clientY - tooltip.offsetHeight - 8;
|
|
76
|
+
tooltip.style.left = tx + 'px';
|
|
77
|
+
tooltip.style.top = (ty < 4 ? e.clientY + 16 : ty) + 'px';
|
|
78
|
+
}
|
|
79
|
+
function hideTooltip() { tooltip.style.opacity = '0'; }
|
|
141
80
|
```
|
|
142
|
-
Dynamic: set `background: conic-gradient(var(--graphit-accent) 0% ${pct}%, var(--graphit-surface-sunken) ${pct}% 100%)`.
|
package/dist/api/client.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export interface ApiError {
|
|
2
|
-
status: number;
|
|
3
|
-
detail: string;
|
|
4
|
-
retry_after_seconds?: number;
|
|
5
|
-
}
|
|
6
|
-
declare class ApiClient {
|
|
7
|
-
request<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
8
|
-
postUnauthed<T>(path: string, body: unknown): Promise<T>;
|
|
9
|
-
get<T>(path: string): Promise<T>;
|
|
10
|
-
post<T>(path: string, body: unknown): Promise<T>;
|
|
11
|
-
put<T>(path: string, body: unknown): Promise<T>;
|
|
12
|
-
delete<T>(path: string): Promise<T>;
|
|
13
|
-
private _fetch;
|
|
14
|
-
}
|
|
15
|
-
export declare const apiClient: ApiClient;
|
|
16
|
-
export {};
|
package/dist/api/client.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { getValidIdToken } from "../auth/token.js";
|
|
2
|
-
const DEFAULT_BASE_URL = "https://api.graphit-app.com";
|
|
3
|
-
const REQUEST_TIMEOUT_MS = 30_000;
|
|
4
|
-
function getBaseUrl() {
|
|
5
|
-
return process.env.GRAPHIT_API_URL || DEFAULT_BASE_URL;
|
|
6
|
-
}
|
|
7
|
-
class ApiClient {
|
|
8
|
-
async request(method, path, body) {
|
|
9
|
-
const { id_token } = await getValidIdToken();
|
|
10
|
-
return this._fetch(method, path, body, {
|
|
11
|
-
Authorization: `Bearer ${id_token}`,
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
async postUnauthed(path, body) {
|
|
15
|
-
return this._fetch("POST", path, body);
|
|
16
|
-
}
|
|
17
|
-
async get(path) {
|
|
18
|
-
return this.request("GET", path);
|
|
19
|
-
}
|
|
20
|
-
async post(path, body) {
|
|
21
|
-
return this.request("POST", path, body);
|
|
22
|
-
}
|
|
23
|
-
async put(path, body) {
|
|
24
|
-
return this.request("PUT", path, body);
|
|
25
|
-
}
|
|
26
|
-
async delete(path) {
|
|
27
|
-
return this.request("DELETE", path);
|
|
28
|
-
}
|
|
29
|
-
async _fetch(method, path, body, extraHeaders) {
|
|
30
|
-
const url = `${getBaseUrl()}${path}`;
|
|
31
|
-
const headers = {
|
|
32
|
-
"Content-Type": "application/json",
|
|
33
|
-
"X-Graphit-Source": "cli",
|
|
34
|
-
...extraHeaders,
|
|
35
|
-
};
|
|
36
|
-
const resp = await fetch(url, {
|
|
37
|
-
method,
|
|
38
|
-
headers,
|
|
39
|
-
body: body ? JSON.stringify(body) : undefined,
|
|
40
|
-
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
41
|
-
});
|
|
42
|
-
if (!resp.ok) {
|
|
43
|
-
let detail = `HTTP ${resp.status}`;
|
|
44
|
-
try {
|
|
45
|
-
const errBody = (await resp.json());
|
|
46
|
-
detail = errBody.detail || detail;
|
|
47
|
-
if (resp.status === 429 && errBody.retry_after_seconds) {
|
|
48
|
-
const err = {
|
|
49
|
-
status: 429,
|
|
50
|
-
detail,
|
|
51
|
-
retry_after_seconds: errBody.retry_after_seconds,
|
|
52
|
-
};
|
|
53
|
-
throw err;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
catch (e) {
|
|
57
|
-
if (e.status === 429)
|
|
58
|
-
throw e;
|
|
59
|
-
}
|
|
60
|
-
const err = { status: resp.status, detail };
|
|
61
|
-
throw err;
|
|
62
|
-
}
|
|
63
|
-
return (await resp.json());
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
export const apiClient = new ApiClient();
|
|
67
|
-
//# sourceMappingURL=client.js.map
|
package/dist/api/client.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export interface StoredCredentials {
|
|
2
|
-
refresh_token: string;
|
|
3
|
-
user_id: string;
|
|
4
|
-
email: string;
|
|
5
|
-
org_id: string;
|
|
6
|
-
org_name: string;
|
|
7
|
-
seat_type: string;
|
|
8
|
-
created_at: string;
|
|
9
|
-
id_token?: string;
|
|
10
|
-
id_token_expiry?: number;
|
|
11
|
-
}
|
|
12
|
-
export declare function getCredentialsPath(): string;
|
|
13
|
-
export declare function readCredentials(): Promise<StoredCredentials | null>;
|
|
14
|
-
export declare function writeCredentials(creds: StoredCredentials): Promise<void>;
|
|
15
|
-
export declare function deleteCredentials(): Promise<void>;
|
package/dist/auth/credentials.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { readFile, writeFile, unlink, mkdir, chmod } from "node:fs/promises";
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { join, dirname } from "node:path";
|
|
4
|
-
import { existsSync } from "node:fs";
|
|
5
|
-
const CREDENTIALS_PATH = join(homedir(), ".graphit", "credentials.json");
|
|
6
|
-
export function getCredentialsPath() {
|
|
7
|
-
return CREDENTIALS_PATH;
|
|
8
|
-
}
|
|
9
|
-
export async function readCredentials() {
|
|
10
|
-
try {
|
|
11
|
-
const raw = await readFile(CREDENTIALS_PATH, "utf-8");
|
|
12
|
-
return JSON.parse(raw);
|
|
13
|
-
}
|
|
14
|
-
catch {
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
export async function writeCredentials(creds) {
|
|
19
|
-
const dir = dirname(CREDENTIALS_PATH);
|
|
20
|
-
if (!existsSync(dir)) {
|
|
21
|
-
await mkdir(dir, { recursive: true });
|
|
22
|
-
}
|
|
23
|
-
await writeFile(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), "utf-8");
|
|
24
|
-
await chmod(CREDENTIALS_PATH, 0o600);
|
|
25
|
-
}
|
|
26
|
-
export async function deleteCredentials() {
|
|
27
|
-
try {
|
|
28
|
-
await unlink(CREDENTIALS_PATH);
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
// File may not exist
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
//# sourceMappingURL=credentials.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/auth/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAcrC,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,kBAAkB,CAAC,CAAC;AAEzE,MAAM,UAAU,kBAAkB;IAChC,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAsB,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAwB;IAC7D,MAAM,GAAG,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACtC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,MAAM,SAAS,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC3E,MAAM,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,qBAAqB;IACvB,CAAC;AACH,CAAC"}
|
package/dist/auth/login.d.ts
DELETED
package/dist/auth/login.js
DELETED
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import { createServer } from "node:http";
|
|
2
|
-
import { randomBytes, createHash } from "node:crypto";
|
|
3
|
-
import { writeCredentials } from "./credentials.js";
|
|
4
|
-
import { apiClient } from "../api/client.js";
|
|
5
|
-
import open from "open";
|
|
6
|
-
const AUTH_TIMEOUT_MS = 120_000;
|
|
7
|
-
function escapeHtml(str) {
|
|
8
|
-
return str
|
|
9
|
-
.replace(/&/g, "&")
|
|
10
|
-
.replace(/</g, "<")
|
|
11
|
-
.replace(/>/g, ">")
|
|
12
|
-
.replace(/"/g, """);
|
|
13
|
-
}
|
|
14
|
-
function renderCallbackPage(variant, title, message) {
|
|
15
|
-
const safeTitle = escapeHtml(title);
|
|
16
|
-
const safeMessage = escapeHtml(message);
|
|
17
|
-
const statusIcon = variant === "success"
|
|
18
|
-
? `<svg width="56" height="56" viewBox="0 0 56 56" fill="none">
|
|
19
|
-
<circle cx="28" cy="28" r="28" fill="#4DB6AC"/>
|
|
20
|
-
<path d="M17 28.5L24 35.5L39 20.5" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
|
21
|
-
</svg>`
|
|
22
|
-
: `<svg width="56" height="56" viewBox="0 0 56 56" fill="none">
|
|
23
|
-
<circle cx="28" cy="28" r="28" fill="#B91C1C"/>
|
|
24
|
-
<path d="M20 20L36 36M36 20L20 36" stroke="#fff" stroke-width="3" stroke-linecap="round"/>
|
|
25
|
-
</svg>`;
|
|
26
|
-
return `<!DOCTYPE html>
|
|
27
|
-
<html lang="en">
|
|
28
|
-
<head>
|
|
29
|
-
<meta charset="utf-8">
|
|
30
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
31
|
-
<title>${safeTitle} - Graphit</title>
|
|
32
|
-
<link rel="stylesheet" href="https://api.fontshare.com/v2/css?f[]=satoshi@400,500,700&display=swap">
|
|
33
|
-
<style>
|
|
34
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
35
|
-
|
|
36
|
-
body {
|
|
37
|
-
font-family: 'Satoshi', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
38
|
-
min-height: 100vh;
|
|
39
|
-
display: flex;
|
|
40
|
-
align-items: center;
|
|
41
|
-
justify-content: center;
|
|
42
|
-
overflow: hidden;
|
|
43
|
-
position: relative;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/* Mesh gradient background matching AuthLayout */
|
|
47
|
-
.bg-base {
|
|
48
|
-
position: fixed; inset: 0;
|
|
49
|
-
background: linear-gradient(135deg, #1a1a1c 0%, #1c2e2b 40%, #222224 70%, #1a2520 100%);
|
|
50
|
-
}
|
|
51
|
-
.bg-orb-bl {
|
|
52
|
-
position: fixed; inset: 0;
|
|
53
|
-
background: radial-gradient(ellipse 70% 65% at 15% 85%, rgba(77,182,172,0.45) 0%, rgba(77,182,172,0.15) 40%, transparent 70%);
|
|
54
|
-
}
|
|
55
|
-
.bg-orb-tr {
|
|
56
|
-
position: fixed; inset: 0;
|
|
57
|
-
background: radial-gradient(ellipse 55% 50% at 85% 15%, rgba(77,182,172,0.35) 0%, rgba(56,142,133,0.10) 45%, transparent 70%);
|
|
58
|
-
}
|
|
59
|
-
.bg-wash {
|
|
60
|
-
position: fixed; inset: 0;
|
|
61
|
-
background: radial-gradient(ellipse 45% 55% at 75% 55%, rgba(128,203,196,0.20) 0%, transparent 60%);
|
|
62
|
-
}
|
|
63
|
-
.bg-pool {
|
|
64
|
-
position: fixed; inset: 0;
|
|
65
|
-
background: radial-gradient(ellipse 50% 45% at 30% 40%, rgba(38,120,110,0.30) 0%, transparent 60%);
|
|
66
|
-
}
|
|
67
|
-
.bg-vignette {
|
|
68
|
-
position: fixed; inset: 0;
|
|
69
|
-
background: radial-gradient(ellipse 75% 75% at 50% 50%, transparent 10%, rgba(16,16,18,0.85) 100%);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.content {
|
|
73
|
-
position: relative;
|
|
74
|
-
z-index: 10;
|
|
75
|
-
text-align: center;
|
|
76
|
-
padding: 48px 40px;
|
|
77
|
-
max-width: 400px;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.card {
|
|
81
|
-
background: rgba(255,255,255,0.08);
|
|
82
|
-
backdrop-filter: blur(24px);
|
|
83
|
-
-webkit-backdrop-filter: blur(24px);
|
|
84
|
-
border: 1px solid rgba(255,255,255,0.12);
|
|
85
|
-
border-radius: 16px;
|
|
86
|
-
padding: 40px 32px;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
.icon { margin-bottom: 20px; }
|
|
90
|
-
|
|
91
|
-
h1 {
|
|
92
|
-
font-size: 20px;
|
|
93
|
-
font-weight: 700;
|
|
94
|
-
color: #fff;
|
|
95
|
-
letter-spacing: -0.02em;
|
|
96
|
-
margin-bottom: 8px;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
p {
|
|
100
|
-
font-size: 14px;
|
|
101
|
-
color: rgba(255,255,255,0.5);
|
|
102
|
-
line-height: 1.6;
|
|
103
|
-
}
|
|
104
|
-
</style>
|
|
105
|
-
</head>
|
|
106
|
-
<body>
|
|
107
|
-
<div class="bg-base"></div>
|
|
108
|
-
<div class="bg-orb-bl"></div>
|
|
109
|
-
<div class="bg-orb-tr"></div>
|
|
110
|
-
<div class="bg-wash"></div>
|
|
111
|
-
<div class="bg-pool"></div>
|
|
112
|
-
<div class="bg-vignette"></div>
|
|
113
|
-
|
|
114
|
-
<div class="content">
|
|
115
|
-
<div class="card">
|
|
116
|
-
<div class="icon">${statusIcon}</div>
|
|
117
|
-
<h1>${safeTitle}</h1>
|
|
118
|
-
<p>${safeMessage}</p>
|
|
119
|
-
</div>
|
|
120
|
-
</div>
|
|
121
|
-
</body>
|
|
122
|
-
</html>`;
|
|
123
|
-
}
|
|
124
|
-
function generateCodeVerifier() {
|
|
125
|
-
return randomBytes(32).toString("base64url");
|
|
126
|
-
}
|
|
127
|
-
function computeCodeChallenge(verifier) {
|
|
128
|
-
return createHash("sha256").update(verifier).digest("base64url");
|
|
129
|
-
}
|
|
130
|
-
function generateState() {
|
|
131
|
-
return randomBytes(32).toString("hex");
|
|
132
|
-
}
|
|
133
|
-
export async function performLogin() {
|
|
134
|
-
const state = generateState();
|
|
135
|
-
const codeVerifier = generateCodeVerifier();
|
|
136
|
-
const codeChallenge = computeCodeChallenge(codeVerifier);
|
|
137
|
-
const { port, result } = await startCallbackServer(state, codeVerifier);
|
|
138
|
-
const callbackUrl = `http://127.0.0.1:${port}/callback`;
|
|
139
|
-
await apiClient.postUnauthed("/api/v1/cli/auth/init", {
|
|
140
|
-
state,
|
|
141
|
-
code_challenge: codeChallenge,
|
|
142
|
-
callback_url: callbackUrl,
|
|
143
|
-
});
|
|
144
|
-
const frontendUrl = process.env.GRAPHIT_FRONTEND_URL || "https://app.graphit-app.com";
|
|
145
|
-
const authUrl = `${frontendUrl}/auth/cli?state=${state}&callback=${encodeURIComponent(callbackUrl)}`;
|
|
146
|
-
console.log("\nOpening browser to log in to Graphit...");
|
|
147
|
-
console.log(` -> ${authUrl}\n`);
|
|
148
|
-
await open(authUrl);
|
|
149
|
-
console.log("Waiting for authentication...\n");
|
|
150
|
-
return result;
|
|
151
|
-
}
|
|
152
|
-
async function startCallbackServer(expectedState, codeVerifier) {
|
|
153
|
-
return new Promise((resolveServer) => {
|
|
154
|
-
let resolveLogin;
|
|
155
|
-
let rejectLogin;
|
|
156
|
-
const loginPromise = new Promise((res, rej) => {
|
|
157
|
-
resolveLogin = res;
|
|
158
|
-
rejectLogin = rej;
|
|
159
|
-
});
|
|
160
|
-
const timeout = setTimeout(() => {
|
|
161
|
-
server.close();
|
|
162
|
-
rejectLogin(new Error("Login timed out. Please try again."));
|
|
163
|
-
}, AUTH_TIMEOUT_MS);
|
|
164
|
-
const server = createServer(async (req, res) => {
|
|
165
|
-
const url = new URL(req.url || "/", `http://127.0.0.1`);
|
|
166
|
-
if (url.pathname !== "/callback") {
|
|
167
|
-
res.writeHead(404);
|
|
168
|
-
res.end("Not found");
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
const code = url.searchParams.get("code");
|
|
172
|
-
const returnedState = url.searchParams.get("state");
|
|
173
|
-
if (returnedState !== expectedState) {
|
|
174
|
-
res.writeHead(400, { "Content-Type": "text/html" });
|
|
175
|
-
res.end(renderCallbackPage("error", "Authentication failed", "State mismatch - possible CSRF attack."));
|
|
176
|
-
clearTimeout(timeout);
|
|
177
|
-
server.close();
|
|
178
|
-
rejectLogin(new Error("State mismatch in callback - possible CSRF."));
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
if (!code) {
|
|
182
|
-
const error = url.searchParams.get("error") || "No authorization code received";
|
|
183
|
-
res.writeHead(400, { "Content-Type": "text/html" });
|
|
184
|
-
res.end(renderCallbackPage("error", "Authentication failed", error));
|
|
185
|
-
clearTimeout(timeout);
|
|
186
|
-
server.close();
|
|
187
|
-
rejectLogin(new Error(error));
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
try {
|
|
191
|
-
const tokenResp = await apiClient.postUnauthed("/api/v1/cli/auth/token", {
|
|
192
|
-
code,
|
|
193
|
-
code_verifier: codeVerifier,
|
|
194
|
-
state: expectedState,
|
|
195
|
-
});
|
|
196
|
-
const creds = {
|
|
197
|
-
refresh_token: tokenResp.refresh_token,
|
|
198
|
-
user_id: tokenResp.user_id,
|
|
199
|
-
email: tokenResp.email,
|
|
200
|
-
org_id: tokenResp.org_id,
|
|
201
|
-
org_name: tokenResp.org_name,
|
|
202
|
-
seat_type: tokenResp.seat_type,
|
|
203
|
-
created_at: new Date().toISOString(),
|
|
204
|
-
};
|
|
205
|
-
await writeCredentials(creds);
|
|
206
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
207
|
-
res.end(renderCallbackPage("success", "Logged in to Graphit", "You can close this tab and return to your terminal."));
|
|
208
|
-
clearTimeout(timeout);
|
|
209
|
-
server.close();
|
|
210
|
-
resolveLogin(creds);
|
|
211
|
-
}
|
|
212
|
-
catch (err) {
|
|
213
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
214
|
-
res.writeHead(500, { "Content-Type": "text/html" });
|
|
215
|
-
res.end(renderCallbackPage("error", "Authentication failed", msg));
|
|
216
|
-
clearTimeout(timeout);
|
|
217
|
-
server.close();
|
|
218
|
-
rejectLogin(err instanceof Error ? err : new Error(msg));
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
server.listen(0, "127.0.0.1", () => {
|
|
222
|
-
const addr = server.address();
|
|
223
|
-
if (addr && typeof addr === "object") {
|
|
224
|
-
resolveServer({ port: addr.port, result: loginPromise });
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
//# sourceMappingURL=login.js.map
|
package/dist/auth/login.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/auth/login.ts"],"names":[],"mappings":"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"}
|
package/dist/auth/token.d.ts
DELETED
package/dist/auth/token.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { readCredentials, writeCredentials } from "./credentials.js";
|
|
2
|
-
const FIREBASE_TOKEN_URL = "https://securetoken.googleapis.com/v1/token";
|
|
3
|
-
const FIREBASE_API_KEY = process.env.GRAPHIT_FIREBASE_API_KEY || "AIzaSyCxahikMb2o0BroW1gqn2aWNad6S6-n-gI";
|
|
4
|
-
const TOKEN_BUFFER_SECONDS = 60;
|
|
5
|
-
export async function getValidIdToken() {
|
|
6
|
-
const creds = await readCredentials();
|
|
7
|
-
if (!creds) {
|
|
8
|
-
throw new Error("Not logged in. Run `graphit auth login` first.");
|
|
9
|
-
}
|
|
10
|
-
if (creds.id_token &&
|
|
11
|
-
creds.id_token_expiry &&
|
|
12
|
-
creds.id_token_expiry > Date.now() / 1000 + TOKEN_BUFFER_SECONDS) {
|
|
13
|
-
return { id_token: creds.id_token, user_id: creds.user_id };
|
|
14
|
-
}
|
|
15
|
-
const resp = await fetch(`${FIREBASE_TOKEN_URL}?key=${FIREBASE_API_KEY}`, {
|
|
16
|
-
method: "POST",
|
|
17
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
18
|
-
body: new URLSearchParams({
|
|
19
|
-
grant_type: "refresh_token",
|
|
20
|
-
refresh_token: creds.refresh_token,
|
|
21
|
-
}),
|
|
22
|
-
signal: AbortSignal.timeout(5000),
|
|
23
|
-
});
|
|
24
|
-
if (!resp.ok) {
|
|
25
|
-
const body = await resp.text();
|
|
26
|
-
if (body.includes("TOKEN_EXPIRED") || body.includes("INVALID_REFRESH_TOKEN")) {
|
|
27
|
-
throw new Error("Session expired. Run `graphit auth login` to re-authenticate.");
|
|
28
|
-
}
|
|
29
|
-
throw new Error(`Token refresh failed (${resp.status}): ${body}`);
|
|
30
|
-
}
|
|
31
|
-
const data = (await resp.json());
|
|
32
|
-
const expiresIn = parseInt(data.expires_in, 10);
|
|
33
|
-
const expiry = Date.now() / 1000 + expiresIn;
|
|
34
|
-
await writeCredentials({
|
|
35
|
-
...creds,
|
|
36
|
-
refresh_token: data.refresh_token || creds.refresh_token,
|
|
37
|
-
id_token: data.id_token,
|
|
38
|
-
id_token_expiry: expiry,
|
|
39
|
-
});
|
|
40
|
-
return { id_token: data.id_token, user_id: data.user_id };
|
|
41
|
-
}
|
|
42
|
-
//# sourceMappingURL=token.js.map
|