@sansynx/erroratlas 0.1.1 → 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/README.md +0 -67
- package/dist/worker/supabase.js +36 -11
- package/dist/worker/ui.js +128 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,70 +89,3 @@ erroratlas.get_playbook
|
|
|
89
89
|
- ErrorAtlas remote stores sanitized searchable incidents and playbooks.
|
|
90
90
|
- Agent keys are shown once and stored remotely only as hashes.
|
|
91
91
|
- Sensitive incident payload snapshots are encrypted before database insert.
|
|
92
|
-
|
|
93
|
-
## Operator setup
|
|
94
|
-
|
|
95
|
-
This section is only for the ErrorAtlas service owner.
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
cp .dev.vars.example .dev.vars
|
|
99
|
-
npm install
|
|
100
|
-
npm run dev
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
Apply the database schema from:
|
|
104
|
-
|
|
105
|
-
```txt
|
|
106
|
-
supabase/schema.sql
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
Set production Worker secrets:
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
npx wrangler secret put PUBLIC_SUPABASE_URL
|
|
113
|
-
npx wrangler secret put PUBLIC_SUPABASE_ANON_KEY
|
|
114
|
-
npx wrangler secret put SUPABASE_SERVICE_ROLE_KEY
|
|
115
|
-
npx wrangler secret put FIELD_ENCRYPTION_KEY
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
Deploy:
|
|
119
|
-
|
|
120
|
-
```bash
|
|
121
|
-
npm run deploy
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Publish the npm package from an npm account or organization you control:
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
npm login
|
|
128
|
-
npm publish --access public
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
The package name is `@sansynx/erroratlas` because the unscoped npm name `erroratlas` is already owned by another package. The CLI command remains `erroratlas`.
|
|
132
|
-
|
|
133
|
-
## Project structure
|
|
134
|
-
|
|
135
|
-
```txt
|
|
136
|
-
src/worker Cloudflare Worker UI and API
|
|
137
|
-
src/mcp local MCP server for coding agents
|
|
138
|
-
src/cli npm package CLI for init and MCP startup
|
|
139
|
-
src/shared redaction and playbook utilities
|
|
140
|
-
supabase service-owner database schema
|
|
141
|
-
templates files copied by npx erroratlas init
|
|
142
|
-
public Worker-served public assets
|
|
143
|
-
scripts maintainer build and smoke-test scripts
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
## Important routes
|
|
147
|
-
|
|
148
|
-
```txt
|
|
149
|
-
GET / landing page
|
|
150
|
-
GET /setup public setup guide
|
|
151
|
-
GET /dashboard protected MCP key console
|
|
152
|
-
GET /llms.txt machine-readable agent guide
|
|
153
|
-
GET /api/health service health
|
|
154
|
-
POST /api/search search approved fixes
|
|
155
|
-
POST /api/agent-keys create or rotate MCP key
|
|
156
|
-
POST /api/incidents capture sanitized error signal
|
|
157
|
-
POST /api/resolutions publish verified resolution
|
|
158
|
-
```
|
package/dist/worker/supabase.js
CHANGED
|
@@ -62,21 +62,12 @@ async function getOrCreateHumanActor(env, user) {
|
|
|
62
62
|
}
|
|
63
63
|
const slug = `personal-${user.id.slice(0, 8)}`;
|
|
64
64
|
const orgName = "Personal atlas";
|
|
65
|
-
const
|
|
66
|
-
method: "POST",
|
|
67
|
-
body: JSON.stringify([{ name: orgName, slug }])
|
|
68
|
-
});
|
|
69
|
-
const orgId = org[0]?.id;
|
|
70
|
-
if (!orgId)
|
|
71
|
-
throw new HttpError(500, "org_create_failed", "Could not create organization.");
|
|
65
|
+
const orgId = await ensurePersonalOrg(env, slug, orgName);
|
|
72
66
|
await supabaseRest(env, "organization_members", {
|
|
73
67
|
method: "POST",
|
|
74
68
|
body: JSON.stringify([{ org_id: orgId, user_id: user.id, role: "owner" }])
|
|
75
69
|
});
|
|
76
|
-
await
|
|
77
|
-
method: "POST",
|
|
78
|
-
body: JSON.stringify([{ org_id: orgId, name: "Default", slug: "default", visibility: "team" }])
|
|
79
|
-
});
|
|
70
|
+
await ensureDefaultProject(env, orgId);
|
|
80
71
|
return {
|
|
81
72
|
kind: "human",
|
|
82
73
|
userId: user.id,
|
|
@@ -87,6 +78,40 @@ async function getOrCreateHumanActor(env, user) {
|
|
|
87
78
|
role: "owner"
|
|
88
79
|
};
|
|
89
80
|
}
|
|
81
|
+
async function ensurePersonalOrg(env, slug, orgName) {
|
|
82
|
+
try {
|
|
83
|
+
const org = await supabaseRest(env, "organizations", {
|
|
84
|
+
method: "POST",
|
|
85
|
+
body: JSON.stringify([{ name: orgName, slug }])
|
|
86
|
+
});
|
|
87
|
+
const orgId = org[0]?.id;
|
|
88
|
+
if (!orgId)
|
|
89
|
+
throw new HttpError(500, "org_create_failed", "Could not create workspace.");
|
|
90
|
+
return orgId;
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
if (!(error instanceof HttpError && /duplicate|unique|23505|409/i.test(error.message)))
|
|
94
|
+
throw error;
|
|
95
|
+
const existing = await supabaseRest(env, `organizations?select=id&slug=eq.${encodeURIComponent(slug)}&limit=1`);
|
|
96
|
+
const orgId = existing[0]?.id;
|
|
97
|
+
if (!orgId)
|
|
98
|
+
throw error;
|
|
99
|
+
return orgId;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function ensureDefaultProject(env, orgId) {
|
|
103
|
+
try {
|
|
104
|
+
await supabaseRest(env, "projects", {
|
|
105
|
+
method: "POST",
|
|
106
|
+
body: JSON.stringify([{ org_id: orgId, name: "Default", slug: "default", visibility: "team" }])
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
if (error instanceof HttpError && /duplicate|unique|23505|409/i.test(error.message))
|
|
111
|
+
return;
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
90
115
|
async function ensureUserProfile(env, user) {
|
|
91
116
|
const existing = await supabaseRest(env, `user_profiles?select=user_id,username,display_name,bio&user_id=eq.${encodeURIComponent(user.id)}&limit=1`);
|
|
92
117
|
if (existing[0])
|
package/dist/worker/ui.js
CHANGED
|
@@ -841,6 +841,129 @@ ul, ol { margin: 0; padding-left: 20px; color: var(--body); }
|
|
|
841
841
|
border-bottom: 0;
|
|
842
842
|
}
|
|
843
843
|
}
|
|
844
|
+
|
|
845
|
+
.nav {
|
|
846
|
+
min-height: 64px;
|
|
847
|
+
margin-top: 18px;
|
|
848
|
+
padding: 10px 12px;
|
|
849
|
+
border: 1px solid var(--line);
|
|
850
|
+
border-radius: 20px;
|
|
851
|
+
background:
|
|
852
|
+
linear-gradient(180deg, color-mix(in srgb, var(--surface) 92%, transparent), color-mix(in srgb, var(--surface-2) 78%, transparent));
|
|
853
|
+
box-shadow: 0 14px 48px rgba(0, 0, 0, 0.08);
|
|
854
|
+
}
|
|
855
|
+
.brand {
|
|
856
|
+
gap: 9px;
|
|
857
|
+
padding-left: 4px;
|
|
858
|
+
}
|
|
859
|
+
.brand::before {
|
|
860
|
+
content: "";
|
|
861
|
+
width: 9px;
|
|
862
|
+
height: 9px;
|
|
863
|
+
border-radius: 999px;
|
|
864
|
+
background: var(--fg);
|
|
865
|
+
box-shadow: 0 0 0 4px var(--surface-2);
|
|
866
|
+
}
|
|
867
|
+
.nav-links {
|
|
868
|
+
gap: 6px;
|
|
869
|
+
}
|
|
870
|
+
.nav-links > a:not(.button),
|
|
871
|
+
.nav-links .button,
|
|
872
|
+
.nav-links .theme-toggle {
|
|
873
|
+
height: 38px;
|
|
874
|
+
min-height: 38px;
|
|
875
|
+
border-radius: 12px;
|
|
876
|
+
}
|
|
877
|
+
.nav-links > a:not(.button) {
|
|
878
|
+
padding: 0 12px;
|
|
879
|
+
color: var(--body);
|
|
880
|
+
}
|
|
881
|
+
.nav-links > a:not(.button):hover {
|
|
882
|
+
color: var(--fg);
|
|
883
|
+
background: var(--surface-2);
|
|
884
|
+
}
|
|
885
|
+
.theme-toggle {
|
|
886
|
+
border-color: transparent;
|
|
887
|
+
background: var(--surface-2);
|
|
888
|
+
}
|
|
889
|
+
.secret-strip {
|
|
890
|
+
display: grid;
|
|
891
|
+
gap: 14px;
|
|
892
|
+
margin: 18px 0 20px;
|
|
893
|
+
padding: 16px;
|
|
894
|
+
border: 1px solid var(--line);
|
|
895
|
+
border-radius: 16px;
|
|
896
|
+
background: var(--surface-2);
|
|
897
|
+
}
|
|
898
|
+
.secret-strip.is-ready {
|
|
899
|
+
border-color: color-mix(in srgb, var(--good) 45%, var(--line));
|
|
900
|
+
background: color-mix(in srgb, var(--green-100) 48%, var(--surface));
|
|
901
|
+
}
|
|
902
|
+
.secret-strip strong {
|
|
903
|
+
display: block;
|
|
904
|
+
margin-bottom: 4px;
|
|
905
|
+
color: var(--fg);
|
|
906
|
+
}
|
|
907
|
+
.secret-strip span {
|
|
908
|
+
color: var(--muted);
|
|
909
|
+
}
|
|
910
|
+
.secret-copy-row {
|
|
911
|
+
display: grid;
|
|
912
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
913
|
+
align-items: center;
|
|
914
|
+
gap: 10px;
|
|
915
|
+
}
|
|
916
|
+
.secret-token {
|
|
917
|
+
display: block;
|
|
918
|
+
min-width: 0;
|
|
919
|
+
padding: 10px 12px;
|
|
920
|
+
overflow: hidden;
|
|
921
|
+
border: 1px solid var(--line);
|
|
922
|
+
border-radius: 10px;
|
|
923
|
+
background: var(--surface);
|
|
924
|
+
color: var(--fg) !important;
|
|
925
|
+
font-family: var(--font-mono);
|
|
926
|
+
font-size: 13px;
|
|
927
|
+
text-overflow: ellipsis;
|
|
928
|
+
white-space: nowrap;
|
|
929
|
+
}
|
|
930
|
+
.secret-copy-row .button {
|
|
931
|
+
height: 40px;
|
|
932
|
+
min-height: 40px;
|
|
933
|
+
border-radius: 10px;
|
|
934
|
+
}
|
|
935
|
+
@media (max-width: 760px) {
|
|
936
|
+
.nav {
|
|
937
|
+
margin-top: 12px;
|
|
938
|
+
border-radius: 18px;
|
|
939
|
+
background: var(--surface);
|
|
940
|
+
box-shadow: 0 16px 52px rgba(0, 0, 0, 0.12);
|
|
941
|
+
}
|
|
942
|
+
.nav.open {
|
|
943
|
+
border-radius: 20px;
|
|
944
|
+
padding: 10px;
|
|
945
|
+
}
|
|
946
|
+
.nav.open .nav-links {
|
|
947
|
+
padding-top: 10px;
|
|
948
|
+
}
|
|
949
|
+
.nav.open .nav-links .button,
|
|
950
|
+
.nav.open .nav-links > a:not(.button),
|
|
951
|
+
.nav.open .theme-toggle {
|
|
952
|
+
justify-content: flex-start;
|
|
953
|
+
height: 44px;
|
|
954
|
+
min-height: 44px;
|
|
955
|
+
padding: 0 10px;
|
|
956
|
+
border-radius: 12px;
|
|
957
|
+
border: 0;
|
|
958
|
+
border-bottom: 0;
|
|
959
|
+
}
|
|
960
|
+
.secret-copy-row {
|
|
961
|
+
grid-template-columns: 1fr;
|
|
962
|
+
}
|
|
963
|
+
.secret-copy-row .button {
|
|
964
|
+
width: 100%;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
844
967
|
</style>`;
|
|
845
968
|
}
|
|
846
969
|
export function renderLandingUi(env) {
|
|
@@ -1985,7 +2108,7 @@ ${sharedCss()}
|
|
|
1985
2108
|
function renderAuth() {
|
|
1986
2109
|
title.textContent = "Sign in";
|
|
1987
2110
|
status.textContent = "Protected console";
|
|
1988
|
-
content.innerHTML = '<div class="panel auth-open-card"><span class="muted">Sign in to mint MCP keys</span><h3>Console access</h3><p>Use GitHub or a one-time email link.
|
|
2111
|
+
content.innerHTML = '<div class="panel auth-open-card"><span class="muted">Sign in to mint MCP keys</span><h3>Console access</h3><p>Use GitHub or a one-time email link. Your workspace is prepared automatically.</p></div><div class="auth-modal" id="authModal" hidden><div class="auth-dialog" role="dialog" aria-modal="true" aria-labelledby="authTitle"><div class="auth-dialog-head"><div><h3 id="authTitle">Enter ErrorAtlas</h3><p>Choose how you want to sign in.</p></div><button class="icon-button" id="closeAuthModal" type="button" aria-label="Close sign in dialog">x</button></div><div class="auth-methods"><button class="button primary" id="githubSignIn" type="button">Continue with GitHub</button><div class="auth-divider">or</div><input id="authEmail" type="email" placeholder="you@example.com" aria-label="Email address"><button class="button" id="sendEmail" type="button">Send magic link</button></div></div></div>';
|
|
1989
2112
|
}
|
|
1990
2113
|
|
|
1991
2114
|
function renderSetupMissing() {
|
|
@@ -1996,7 +2119,7 @@ ${sharedCss()}
|
|
|
1996
2119
|
}
|
|
1997
2120
|
|
|
1998
2121
|
async function signInWithGitHub() {
|
|
1999
|
-
if (!state.config || !state.config.supabaseUrl) return setMessage("
|
|
2122
|
+
if (!state.config || !state.config.supabaseUrl) return setMessage("Sign-in is not configured yet.");
|
|
2000
2123
|
const redirectTo = location.origin + "/dashboard";
|
|
2001
2124
|
location.assign(supabaseBase() + "/auth/v1/authorize?provider=github&redirect_to=" + encodeURIComponent(redirectTo));
|
|
2002
2125
|
}
|
|
@@ -2010,6 +2133,7 @@ ${sharedCss()}
|
|
|
2010
2133
|
body: JSON.stringify({ email: email, create_user: true })
|
|
2011
2134
|
});
|
|
2012
2135
|
if (!response.ok) throw new Error(await response.text());
|
|
2136
|
+
closeAuthModal();
|
|
2013
2137
|
setMessage("Magic link sent. Check your inbox for " + email + ".", "success");
|
|
2014
2138
|
}
|
|
2015
2139
|
|
|
@@ -2186,10 +2310,8 @@ ${sharedCss()}
|
|
|
2186
2310
|
renderTopBar();
|
|
2187
2311
|
render();
|
|
2188
2312
|
} catch (error) {
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
state.data = null;
|
|
2192
|
-
setMessage(error instanceof Error ? error.message : "Dashboard failed to load.");
|
|
2313
|
+
state.data = { agentKeys: [], setupWarning: "Your workspace is still being prepared. Refresh once; if it continues, sign out and sign in again." };
|
|
2314
|
+
setMessage(error instanceof Error ? error.message : "Workspace could not finish loading.");
|
|
2193
2315
|
renderTopBar();
|
|
2194
2316
|
render();
|
|
2195
2317
|
}
|