@heychunky/core 0.1.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.
Files changed (118) hide show
  1. package/README.md +2 -0
  2. package/dist/admin/apps.d.ts +49 -0
  3. package/dist/admin/apps.d.ts.map +1 -0
  4. package/dist/admin/apps.js +97 -0
  5. package/dist/admin/apps.js.map +1 -0
  6. package/dist/admin/index.d.ts +26 -0
  7. package/dist/admin/index.d.ts.map +1 -0
  8. package/dist/admin/index.js +27 -0
  9. package/dist/admin/index.js.map +1 -0
  10. package/dist/admin/master.d.ts +9 -0
  11. package/dist/admin/master.d.ts.map +1 -0
  12. package/dist/admin/master.js +14 -0
  13. package/dist/admin/master.js.map +1 -0
  14. package/dist/admin/provision.d.ts +42 -0
  15. package/dist/admin/provision.d.ts.map +1 -0
  16. package/dist/admin/provision.js +157 -0
  17. package/dist/admin/provision.js.map +1 -0
  18. package/dist/admin/run.d.ts +89 -0
  19. package/dist/admin/run.d.ts.map +1 -0
  20. package/dist/admin/run.js +218 -0
  21. package/dist/admin/run.js.map +1 -0
  22. package/dist/admin/secrets/infisical.d.ts +45 -0
  23. package/dist/admin/secrets/infisical.d.ts.map +1 -0
  24. package/dist/admin/secrets/infisical.js +182 -0
  25. package/dist/admin/secrets/infisical.js.map +1 -0
  26. package/dist/admin/services/agentmail.d.ts +16 -0
  27. package/dist/admin/services/agentmail.d.ts.map +1 -0
  28. package/dist/admin/services/agentmail.js +43 -0
  29. package/dist/admin/services/agentmail.js.map +1 -0
  30. package/dist/admin/services/ai-gateway.d.ts +13 -0
  31. package/dist/admin/services/ai-gateway.d.ts.map +1 -0
  32. package/dist/admin/services/ai-gateway.js +32 -0
  33. package/dist/admin/services/ai-gateway.js.map +1 -0
  34. package/dist/admin/services/bunny.d.ts +16 -0
  35. package/dist/admin/services/bunny.d.ts.map +1 -0
  36. package/dist/admin/services/bunny.js +59 -0
  37. package/dist/admin/services/bunny.js.map +1 -0
  38. package/dist/admin/services/cloudflare.d.ts +42 -0
  39. package/dist/admin/services/cloudflare.d.ts.map +1 -0
  40. package/dist/admin/services/cloudflare.js +97 -0
  41. package/dist/admin/services/cloudflare.js.map +1 -0
  42. package/dist/admin/services/db.d.ts +16 -0
  43. package/dist/admin/services/db.d.ts.map +1 -0
  44. package/dist/admin/services/db.js +81 -0
  45. package/dist/admin/services/db.js.map +1 -0
  46. package/dist/admin/services/elevenlabs.d.ts +22 -0
  47. package/dist/admin/services/elevenlabs.d.ts.map +1 -0
  48. package/dist/admin/services/elevenlabs.js +45 -0
  49. package/dist/admin/services/elevenlabs.js.map +1 -0
  50. package/dist/admin/services/firecrawl.d.ts +13 -0
  51. package/dist/admin/services/firecrawl.d.ts.map +1 -0
  52. package/dist/admin/services/firecrawl.js +36 -0
  53. package/dist/admin/services/firecrawl.js.map +1 -0
  54. package/dist/admin/services/fireworks.d.ts +9 -0
  55. package/dist/admin/services/fireworks.d.ts.map +1 -0
  56. package/dist/admin/services/fireworks.js +31 -0
  57. package/dist/admin/services/fireworks.js.map +1 -0
  58. package/dist/admin/services/github.d.ts +26 -0
  59. package/dist/admin/services/github.d.ts.map +1 -0
  60. package/dist/admin/services/github.js +164 -0
  61. package/dist/admin/services/github.js.map +1 -0
  62. package/dist/admin/services/neon.d.ts +42 -0
  63. package/dist/admin/services/neon.d.ts.map +1 -0
  64. package/dist/admin/services/neon.js +157 -0
  65. package/dist/admin/services/neon.js.map +1 -0
  66. package/dist/admin/services/resend.d.ts +38 -0
  67. package/dist/admin/services/resend.d.ts.map +1 -0
  68. package/dist/admin/services/resend.js +81 -0
  69. package/dist/admin/services/resend.js.map +1 -0
  70. package/dist/admin/services/togetherai.d.ts +10 -0
  71. package/dist/admin/services/togetherai.d.ts.map +1 -0
  72. package/dist/admin/services/togetherai.js +36 -0
  73. package/dist/admin/services/togetherai.js.map +1 -0
  74. package/dist/admin/services/vercel.d.ts +66 -0
  75. package/dist/admin/services/vercel.d.ts.map +1 -0
  76. package/dist/admin/services/vercel.js +209 -0
  77. package/dist/admin/services/vercel.js.map +1 -0
  78. package/dist/admin/syncenv.d.ts +12 -0
  79. package/dist/admin/syncenv.d.ts.map +1 -0
  80. package/dist/admin/syncenv.js +114 -0
  81. package/dist/admin/syncenv.js.map +1 -0
  82. package/dist/admin/users.d.ts +27 -0
  83. package/dist/admin/users.d.ts.map +1 -0
  84. package/dist/admin/users.js +48 -0
  85. package/dist/admin/users.js.map +1 -0
  86. package/dist/config/environments.d.ts +50 -0
  87. package/dist/config/environments.d.ts.map +1 -0
  88. package/dist/config/environments.js +62 -0
  89. package/dist/config/environments.js.map +1 -0
  90. package/dist/config/names.d.ts +45 -0
  91. package/dist/config/names.d.ts.map +1 -0
  92. package/dist/config/names.js +75 -0
  93. package/dist/config/names.js.map +1 -0
  94. package/dist/http/errors.d.ts +45 -0
  95. package/dist/http/errors.d.ts.map +1 -0
  96. package/dist/http/errors.js +70 -0
  97. package/dist/http/errors.js.map +1 -0
  98. package/dist/http/request.d.ts +36 -0
  99. package/dist/http/request.d.ts.map +1 -0
  100. package/dist/http/request.js +73 -0
  101. package/dist/http/request.js.map +1 -0
  102. package/dist/index.d.ts +15 -0
  103. package/dist/index.d.ts.map +1 -0
  104. package/dist/index.js +15 -0
  105. package/dist/index.js.map +1 -0
  106. package/dist/registry.d.ts +66 -0
  107. package/dist/registry.d.ts.map +1 -0
  108. package/dist/registry.js +137 -0
  109. package/dist/registry.js.map +1 -0
  110. package/dist/secrets/store.d.ts +50 -0
  111. package/dist/secrets/store.d.ts.map +1 -0
  112. package/dist/secrets/store.js +71 -0
  113. package/dist/secrets/store.js.map +1 -0
  114. package/dist/services/types.d.ts +39 -0
  115. package/dist/services/types.d.ts.map +1 -0
  116. package/dist/services/types.js +28 -0
  117. package/dist/services/types.js.map +1 -0
  118. package/package.json +40 -0
@@ -0,0 +1,164 @@
1
+ /**
2
+ * GitHub — repos, branches, issues, project boards.
3
+ *
4
+ * The factory's token is account-wide and spans several organisations, which is
5
+ * deliberate: a fine-grained PAT has exactly one resource owner and cannot cover
6
+ * heychunkydev, carmelcity, calinnov and the user account at once. Breadth is a
7
+ * requirement of that, so containment lives here — destructive calls refuse
8
+ * without an explicit confirmation.
9
+ */
10
+ import { request, requestWithHeaders } from "../../http/request.js";
11
+ import { NotFoundError } from "../../http/errors.js";
12
+ import { matchByName } from "../../config/names.js";
13
+ import { probe, probeAccess } from "../../services/types.js";
14
+ const GH = "https://api.github.com";
15
+ const VERSION = { "x-github-api-version": "2022-11-28" };
16
+ /** Scopes worth spelling out, dangerous first. Classic PATs only. */
17
+ const SCOPE_MEANING = [
18
+ ["admin:enterprise", "administer the enterprise account"],
19
+ ["admin:org", "administer the organisation, its members and settings"],
20
+ ["delete_repo", "delete any repository"],
21
+ ["repo", "read and write every repository, private included"],
22
+ ["workflow", "create and update Actions workflows"],
23
+ ["project", "read and write project boards"],
24
+ ["write:packages", "publish and overwrite packages"],
25
+ ];
26
+ export function github(token) {
27
+ const creds = { token };
28
+ const opts = { credentials: creds, headers: VERSION };
29
+ async function listRepos(affiliation = "owner,organization_member") {
30
+ const out = [];
31
+ for (let page = 1;; page++) {
32
+ const batch = await request(`${GH}/user/repos?affiliation=${affiliation}&per_page=100&page=${page}`, opts);
33
+ out.push(...batch);
34
+ if (batch.length < 100)
35
+ break;
36
+ }
37
+ return out;
38
+ }
39
+ /** Public repos on the authenticated user. The policy expects none. */
40
+ async function listPublicRepos() {
41
+ return request(`${GH}/user/repos?visibility=public&affiliation=owner&per_page=100`, opts);
42
+ }
43
+ /** Who this token authenticates as. The default owner for a new repo. */
44
+ const login = async () => (await request(`${GH}/user`, opts)).login;
45
+ /**
46
+ * One repository, or undefined.
47
+ *
48
+ * Provisioning asks this before creating anything, which is what makes a
49
+ * half-finished run safe to repeat. A 404 is an answer here, not a failure.
50
+ */
51
+ async function findRepo(fullName) {
52
+ try {
53
+ return await request(`${GH}/repos/${fullName}`, opts);
54
+ }
55
+ catch (err) {
56
+ if (err instanceof NotFoundError)
57
+ return undefined;
58
+ throw err;
59
+ }
60
+ }
61
+ /**
62
+ * A repository for an app, trying the forms one might have been named in.
63
+ *
64
+ * Exact first, because that is right for anything the factory created and
65
+ * costs one request. Only when that misses does it list the owner's repos and
66
+ * match on name variants — which is what finds `carmel.city` when the app is
67
+ * called `carmel-city`, or the reverse.
68
+ *
69
+ * Worth the extra call: a false "missing" in a provisioning tool is the
70
+ * dangerous direction, because the next step is to create what is already
71
+ * there under another name.
72
+ */
73
+ async function findRepoFor(app, org) {
74
+ const owner = org ?? (await login());
75
+ const exact = await findRepo(`${owner}/${app}`);
76
+ if (exact)
77
+ return exact;
78
+ const mine = (await listRepos()).filter((r) => r.full_name.startsWith(`${owner}/`));
79
+ return matchByName(app, mine, (r) => r.name);
80
+ }
81
+ /**
82
+ * Create a private repo, in an organisation or on the user.
83
+ * Private is not a parameter — the factory has no use for a public repo, and
84
+ * making it an option is how one gets created by accident.
85
+ */
86
+ async function createRepo(name, o = {}) {
87
+ const url = o.org ? `${GH}/orgs/${o.org}/repos` : `${GH}/user/repos`;
88
+ return request(url, {
89
+ ...opts, method: "POST",
90
+ body: { name, private: true, auto_init: true, description: o.description },
91
+ });
92
+ }
93
+ /**
94
+ * Delete a repository. Takes issues, pull requests, releases and history
95
+ * with it; GitHub's restore window closes the moment the name is reused.
96
+ *
97
+ * The `confirmed` flag is required by the type, so this cannot be reached by
98
+ * a caller that has not thought about it — the guarantee holds whatever a
99
+ * skill file or a prompt says.
100
+ */
101
+ async function deleteRepo(fullName, o) {
102
+ if (!o?.confirmed)
103
+ throw new Error(`Refusing to delete ${fullName} without confirmation`);
104
+ await request(`${GH}/repos/${fullName}`, { ...opts, method: "DELETE" });
105
+ }
106
+ async function createBranch(fullName, branch, from) {
107
+ const ref = await request(`${GH}/repos/${fullName}/git/ref/heads/${from}`, opts);
108
+ await request(`${GH}/repos/${fullName}/git/refs`, {
109
+ ...opts, method: "POST",
110
+ body: { ref: `refs/heads/${branch}`, sha: ref.object.sha },
111
+ });
112
+ }
113
+ return {
114
+ login,
115
+ listRepos,
116
+ listPublicRepos,
117
+ findRepo,
118
+ findRepoFor,
119
+ createRepo,
120
+ deleteRepo,
121
+ createBranch,
122
+ async status() {
123
+ return probe("github", async () => {
124
+ const me = await request(`${GH}/user`, opts);
125
+ const pub = await listPublicRepos();
126
+ return `${me.login} · ${pub.length} public repo${pub.length === 1 ? "" : "s"}` +
127
+ (pub.length ? " ⚠️ policy is zero" : "");
128
+ });
129
+ },
130
+ async access() {
131
+ return probeAccess("github", async () => {
132
+ const { data: me, headers } = await requestWithHeaders(`${GH}/user`, opts);
133
+ // Populated only for classic PATs. Fine-grained tokens and app tokens
134
+ // omit it, and GitHub offers no other way to read their permissions.
135
+ const raw = headers.get("x-oauth-scopes");
136
+ const scopes = (raw ?? "").split(",").map((s) => s.trim()).filter(Boolean);
137
+ const expires = headers.get("github-authentication-token-expiration");
138
+ const notes = [];
139
+ if (scopes.length) {
140
+ notes.push(`classic PAT, ${scopes.length} scopes, authenticated as ${me.login}`);
141
+ if (scopes.includes("delete_repo"))
142
+ notes.push("holds delete_repo — this key can delete repositories");
143
+ if (scopes.includes("admin:org"))
144
+ notes.push("holds organisation admin, beyond what any command uses");
145
+ }
146
+ else {
147
+ notes.push(`fine-grained PAT or app token as ${me.login}`);
148
+ notes.push("GitHub does not expose fine-grained permissions over the API — rights unknown");
149
+ }
150
+ notes.push(expires ? `expires ${expires.slice(0, 10)}` : "does not expire");
151
+ const repos = await listRepos();
152
+ return {
153
+ evidence: scopes.length ? "scopes" : "identity",
154
+ scopes,
155
+ can: scopes.length
156
+ ? SCOPE_MEANING.filter(([s]) => scopes.includes(s)).map(([, m]) => m)
157
+ : [`reach ${repos.length} repositories`],
158
+ notes,
159
+ };
160
+ });
161
+ },
162
+ };
163
+ }
164
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../../src/admin/services/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { type ServiceAccess, type ServiceStatus } from "../../services/types.ts";
2
+ export interface NeonProject {
3
+ id: string;
4
+ name: string;
5
+ region_id: string;
6
+ pg_version: number;
7
+ }
8
+ export interface NeonBranch {
9
+ id: string;
10
+ name: string;
11
+ parent_id?: string;
12
+ default?: boolean;
13
+ current_state?: string;
14
+ }
15
+ export declare function neon(token: string): {
16
+ organizations: () => Promise<{
17
+ id: string;
18
+ name: string;
19
+ plan: string;
20
+ }[]>;
21
+ projects: () => Promise<NeonProject[]>;
22
+ findProject: (name: string) => Promise<NeonProject | undefined>;
23
+ createProject: (name: string, opts?: {
24
+ orgId?: string;
25
+ region?: string;
26
+ }) => Promise<NeonProject>;
27
+ branches: (projectId: string) => Promise<NeonBranch[]>;
28
+ createBranch: (projectId: string, name: string, parentId?: string) => Promise<NeonBranch>;
29
+ resetFromParent: (projectId: string, branchId: string, opts: {
30
+ confirmed: true;
31
+ }) => Promise<void>;
32
+ deleteBranch: (projectId: string, branchId: string, opts: {
33
+ confirmed: true;
34
+ }) => Promise<void>;
35
+ connectionUri: (projectId: string, branchId: string, opts?: {
36
+ database?: string;
37
+ role?: string;
38
+ }) => Promise<string>;
39
+ status(): Promise<ServiceStatus>;
40
+ access(): Promise<ServiceAccess>;
41
+ };
42
+ //# sourceMappingURL=neon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neon.d.ts","sourceRoot":"","sources":["../../../src/admin/services/neon.ts"],"names":[],"mappings":"AAOA,OAAO,EAAsB,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAIrG,MAAM,WAAW,WAAW;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE;AAChG,MAAM,WAAW,UAAU;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE;AAEvH,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM;;YAKQ,MAAM;cAAQ,MAAM;cAAQ,MAAM;;oBAE/C,OAAO,CAAC,WAAW,EAAE,CAAC;wBAShB,MAAM,KAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;0BAelE,MAAM,SAAQ;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,KACtD,OAAO,CAAC,WAAW,CAAC;0BAYY,MAAM;8BAY5B,MAAM,QAAQ,MAAM,aAAa,MAAM,KACjD,OAAO,CAAC,UAAU,CAAC;iCAwDT,MAAM,YAAY,MAAM,QAAQ;QAAE,SAAS,EAAE,IAAI,CAAA;KAAE,KAC7D,OAAO,CAAC,IAAI,CAAC;8BA9BH,MAAM,YAAY,MAAM,QAAQ;QAAE,SAAS,EAAE,IAAI,CAAA;KAAE,KAC7D,OAAO,CAAC,IAAI,CAAC;+BAYH,MAAM,YAAY,MAAM,SAC7B;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KACzC,OAAO,CAAC,MAAM,CAAC;cAoCA,OAAO,CAAC,aAAa,CAAC;cAOtB,OAAO,CAAC,aAAa,CAAC;EAyBzC"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Neon — the control plane. Projects, branches, endpoints.
3
+ *
4
+ * App data goes through ../../services/neon.ts, which reaches for a connection
5
+ * string and never for this key.
6
+ */
7
+ import { request } from "../../http/request.js";
8
+ import { probe, probeAccess } from "../../services/types.js";
9
+ const API = "https://console.neon.tech/api/v2";
10
+ export function neon(token) {
11
+ const creds = { token };
12
+ const o = { credentials: creds };
13
+ const organizations = async () => (await request(`${API}/users/me/organizations`, o)).organizations;
14
+ async function projects() {
15
+ const out = [];
16
+ for (const org of await organizations()) {
17
+ const r = await request(`${API}/projects?org_id=${org.id}`, o);
18
+ out.push(...r.projects);
19
+ }
20
+ return out;
21
+ }
22
+ const findProject = async (name) => (await projects()).find((p) => p.name === name);
23
+ /**
24
+ * Create a project, with its default branch and endpoint.
25
+ *
26
+ * The region follows whatever the account already uses rather than a
27
+ * hardcoded default — an app whose database sits on another continent from
28
+ * its siblings is a latency bug nobody goes looking for.
29
+ *
30
+ * Neon's response includes live connection URIs, password and all. Only the
31
+ * project's identity leaves this function; a connection string is something a
32
+ * caller asks for deliberately.
33
+ */
34
+ async function createProject(name, opts = {}) {
35
+ const existing = await projects();
36
+ const org = opts.orgId ?? (await organizations())[0]?.id;
37
+ if (!org)
38
+ throw new Error("No Neon organisation this key can create in");
39
+ const region = opts.region ?? existing[0]?.region_id ?? "aws-us-east-2";
40
+ const { project: p } = await request(`${API}/projects`, {
41
+ ...o, method: "POST",
42
+ body: { project: { name, org_id: org, region_id: region } },
43
+ });
44
+ return { id: p.id, name: p.name, region_id: p.region_id, pg_version: p.pg_version };
45
+ }
46
+ const branches = async (projectId) => (await request(`${API}/projects/${projectId}/branches`, o)).branches ?? [];
47
+ /**
48
+ * Create a branch, with a compute endpoint so it can be connected to.
49
+ *
50
+ * Neon will happily create a branch with no endpoint — it costs nothing and
51
+ * is unreachable, which is a confusing thing to hand someone as a working
52
+ * environment. The endpoint is what makes it a database rather than a
53
+ * snapshot.
54
+ */
55
+ async function createBranch(projectId, name, parentId) {
56
+ const { branch } = await request(`${API}/projects/${projectId}/branches`, {
57
+ ...o, method: "POST",
58
+ body: {
59
+ branch: { name, ...(parentId ? { parent_id: parentId } : {}) },
60
+ endpoints: [{ type: "read_write" }],
61
+ },
62
+ });
63
+ return { id: branch.id, name: branch.name, parent_id: branch.parent_id, default: branch.default };
64
+ }
65
+ /**
66
+ * Refresh one branch from its parent — copy-on-write, no dump, no transfer.
67
+ *
68
+ * Two guards live here rather than in a skill, so they hold whatever a future
69
+ * caller believes: the default branch (production) can never be the target,
70
+ * and the branch must have a parent to be reset from. `confirmed` is required
71
+ * because this discards everything in the target.
72
+ */
73
+ /**
74
+ * Delete a branch and everything in it.
75
+ *
76
+ * Neon refuses to delete a default branch, which is the protection that
77
+ * matters — production cannot be removed by naming it.
78
+ */
79
+ async function deleteBranch(projectId, branchId, opts) {
80
+ if (!opts?.confirmed)
81
+ throw new Error(`Refusing to delete ${branchId} without confirmation`);
82
+ await request(`${API}/projects/${projectId}/branches/${branchId}`, { ...o, method: "DELETE" });
83
+ }
84
+ /**
85
+ * The connection string for a branch.
86
+ *
87
+ * A credential, so it is never rendered — the only caller is the one that
88
+ * writes it into the vault, and the CLI has no action that would print it.
89
+ */
90
+ async function connectionUri(projectId, branchId, opts = {}) {
91
+ const query = new URLSearchParams({
92
+ branch_id: branchId,
93
+ database_name: opts.database ?? "neondb",
94
+ role_name: opts.role ?? "neondb_owner",
95
+ });
96
+ const body = await request(`${API}/projects/${projectId}/connection_uri?${query}`, o);
97
+ if (!body.uri)
98
+ throw new Error(`Neon returned no connection string for ${branchId}`);
99
+ return body.uri;
100
+ }
101
+ async function resetFromParent(projectId, branchId, opts) {
102
+ if (!opts?.confirmed)
103
+ throw new Error(`Refusing to reset ${branchId} without confirmation`);
104
+ const all = await branches(projectId);
105
+ const target = all.find((b) => b.id === branchId || b.name === branchId);
106
+ if (!target)
107
+ throw new Error(`No branch "${branchId}" in project ${projectId}`);
108
+ if (target.default)
109
+ throw new Error(`Refusing to reset "${target.name}" — it is the default branch`);
110
+ if (!target.parent_id)
111
+ throw new Error(`Branch "${target.name}" has no parent to reset from`);
112
+ await request(`${API}/projects/${projectId}/branches/${target.id}/reset_to_parent`, { ...o, method: "POST" });
113
+ }
114
+ return {
115
+ organizations,
116
+ projects,
117
+ findProject,
118
+ createProject,
119
+ branches,
120
+ createBranch,
121
+ resetFromParent,
122
+ deleteBranch,
123
+ connectionUri,
124
+ async status() {
125
+ return probe("neon", async () => {
126
+ const [orgs, p] = await Promise.all([organizations(), projects()]);
127
+ return `${p.length} project${p.length === 1 ? "" : "s"} across ${orgs.map((x) => x.name).join(", ")}`;
128
+ });
129
+ },
130
+ async access() {
131
+ return probeAccess("neon", async () => {
132
+ const orgs = await organizations();
133
+ const p = await projects();
134
+ // A personal key can read /users/me; an organisation key cannot.
135
+ let scope = "organisation key";
136
+ try {
137
+ const me = await request(`${API}/users/me`, o);
138
+ scope = `personal key, acting as ${me.email}`;
139
+ }
140
+ catch { /* organisation-scoped, which is narrower */ }
141
+ return {
142
+ evidence: "identity",
143
+ can: [
144
+ `act across ${orgs.map((x) => `${x.name} (${x.plan})`).join(", ")}`,
145
+ `see ${p.length} projects`,
146
+ "create and delete projects, branches and endpoints",
147
+ ],
148
+ notes: [
149
+ scope,
150
+ "app data is read through a connection string, not this key",
151
+ ],
152
+ };
153
+ });
154
+ },
155
+ };
156
+ }
157
+ //# sourceMappingURL=neon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neon.js","sourceRoot":"","sources":["../../../src/admin/services/neon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { type ServiceAccess, type ServiceStatus } from "../../services/types.ts";
2
+ export interface Domain {
3
+ id: string;
4
+ name: string;
5
+ status: string;
6
+ region?: string;
7
+ }
8
+ /** A record the domain's DNS has to carry before Resend will send from it. */
9
+ export interface DomainRecord {
10
+ record: string;
11
+ name: string;
12
+ type: string;
13
+ value: string;
14
+ ttl?: string;
15
+ priority?: number;
16
+ }
17
+ export declare function resend(token: string): {
18
+ domains: () => Promise<Domain[]>;
19
+ apiKeys: () => Promise<{
20
+ id: string;
21
+ name: string;
22
+ }[]>;
23
+ createDomain: (name: string) => Promise<Domain & {
24
+ records: DomainRecord[];
25
+ }>;
26
+ verifyDomain: (id: string) => Promise<{
27
+ id: string;
28
+ object?: string;
29
+ }>;
30
+ createSendingKey: (name: string, domainId: string) => Promise<{
31
+ id: string;
32
+ token: string;
33
+ }>;
34
+ verifiedDomains: () => Promise<Domain[]>;
35
+ status(): Promise<ServiceStatus>;
36
+ access(): Promise<ServiceAccess>;
37
+ };
38
+ //# sourceMappingURL=resend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resend.d.ts","sourceRoot":"","sources":["../../../src/admin/services/resend.ts"],"names":[],"mappings":"AAIA,OAAO,EAAsB,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAErG,MAAM,WAAW,MAAM;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;AAErF,8EAA8E;AAC9E,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM;;;YAKuB,MAAM;cAAQ,MAAM;;yBAS3C,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG;QAAE,OAAO,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;uBAwBxD,MAAM,KAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;6BATzE,MAAM,YAAY,MAAM,KAC7B,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;;cAsBvB,OAAO,CAAC,aAAa,CAAC;cAStB,OAAO,CAAC,aAAa,CAAC;EAsBzC"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Resend — transactional email.
3
+ */
4
+ import { request } from "../../http/request.js";
5
+ import { probe, probeAccess } from "../../services/types.js";
6
+ export function resend(token) {
7
+ const creds = { token };
8
+ const BASE = "https://api.resend.com";
9
+ const domains = async () => (await request(`${BASE}/domains`, { credentials: creds })).data;
10
+ const apiKeys = async () => (await request(`${BASE}/api-keys`, { credentials: creds })).data;
11
+ /**
12
+ * Add a sending domain, and return the DNS it now needs.
13
+ *
14
+ * Resend answers with the records rather than a bare id, because a domain it
15
+ * cannot verify is not a sending domain yet — the useful result of creating
16
+ * one is the work it leaves behind.
17
+ */
18
+ async function createDomain(name) {
19
+ return await request(`${BASE}/domains`, {
20
+ credentials: creds, method: "POST", body: { name },
21
+ });
22
+ }
23
+ /**
24
+ * A key that can only send, and only from one domain.
25
+ *
26
+ * The factory's own key can read every key on the account and add domains.
27
+ * An app needs neither, and giving it one would put a credential that can
28
+ * reach every other app inside a workload — so a scoped key is what an app
29
+ * gets, and this is where the scoping is stated rather than remembered.
30
+ */
31
+ async function createSendingKey(name, domainId) {
32
+ return await request(`${BASE}/api-keys`, {
33
+ credentials: creds, method: "POST",
34
+ body: { name, permission: "sending_access", domain_id: domainId },
35
+ });
36
+ }
37
+ /** Ask Resend to check the DNS. Verification is its answer, not ours. */
38
+ async function verifyDomain(id) {
39
+ return await request(`${BASE}/domains/${id}/verify`, {
40
+ credentials: creds, method: "POST", body: {},
41
+ });
42
+ }
43
+ return {
44
+ domains,
45
+ apiKeys,
46
+ createDomain,
47
+ verifyDomain,
48
+ createSendingKey,
49
+ verifiedDomains: async () => (await domains()).filter((d) => d.status === "verified"),
50
+ async status() {
51
+ return probe("resend", async () => {
52
+ const d = await domains();
53
+ const unverified = d.filter((x) => x.status !== "verified").length;
54
+ return `${d.filter((x) => x.status === "verified").length} verified domains` +
55
+ (unverified ? ` ⚠️ ${unverified} unverified` : "");
56
+ });
57
+ },
58
+ async access() {
59
+ return probeAccess("resend", async () => {
60
+ const d = await domains();
61
+ // Listing API keys requires full access — a sending-only key cannot.
62
+ // So reaching this line is itself the proof.
63
+ const keys = await apiKeys();
64
+ return {
65
+ evidence: "identity",
66
+ can: [
67
+ "list and manage API keys for the whole account",
68
+ `send from ${d.filter((x) => x.status === "verified").length} verified domains`,
69
+ "read and add sending domains",
70
+ ],
71
+ notes: [
72
+ "listing API keys requires full access — a sending-only key cannot, so this one is full access",
73
+ `${keys.length} keys on the account: ${keys.map((k) => k.name).join(", ")}`,
74
+ "per-app keys are named per app, which is a convention and not an isolation boundary",
75
+ ],
76
+ };
77
+ });
78
+ },
79
+ };
80
+ }
81
+ //# sourceMappingURL=resend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resend.js","sourceRoot":"","sources":["../../../src/admin/services/resend.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { type ServiceAccess, type ServiceStatus } from "../../services/types.ts";
2
+ export declare function togetherai(token: string): {
3
+ models: () => Promise<{
4
+ id: string;
5
+ type?: string;
6
+ }[]>;
7
+ status(): Promise<ServiceStatus>;
8
+ access(): Promise<ServiceAccess>;
9
+ };
10
+ //# sourceMappingURL=togetherai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"togetherai.d.ts","sourceRoot":"","sources":["../../../src/admin/services/togetherai.ts"],"names":[],"mappings":"AAOA,OAAO,EAAsB,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAErG,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM;kBAIb,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;cAM/C,OAAO,CAAC,aAAa,CAAC;cAOtB,OAAO,CAAC,aAAa,CAAC;EAWzC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Together AI — inference.
3
+ *
4
+ * One account-wide key. Together declares nothing about a key's rights, so the
5
+ * access report says `reachability` and nothing more.
6
+ */
7
+ import { request } from "../../http/request.js";
8
+ import { probe, probeAccess } from "../../services/types.js";
9
+ export function togetherai(token) {
10
+ const creds = { token };
11
+ const BASE = "https://api.together.xyz/v1";
12
+ async function models() {
13
+ return request(`${BASE}/models`, { credentials: creds });
14
+ }
15
+ return {
16
+ models,
17
+ async status() {
18
+ return probe("togetherai", async () => {
19
+ const all = await models();
20
+ const chat = all.filter((m) => m.type === "chat").length;
21
+ return `${all.length} models, ${chat} chat`;
22
+ });
23
+ },
24
+ async access() {
25
+ return probeAccess("togetherai", async () => {
26
+ const all = await models();
27
+ return {
28
+ evidence: "reachability",
29
+ can: [`run inference against ${all.length} models`],
30
+ notes: ["account-wide key; Together declares nothing about a key's rights"],
31
+ };
32
+ });
33
+ },
34
+ };
35
+ }
36
+ //# sourceMappingURL=togetherai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"togetherai.js","sourceRoot":"","sources":["../../../src/admin/services/togetherai.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,OAAO,EAAoB,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,KAAK,EAAE,WAAW,EAA0C,MAAM,yBAAyB,CAAC;AAErG,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,MAAM,KAAK,GAAgB,EAAE,KAAK,EAAE,CAAC;IACrC,MAAM,IAAI,GAAG,6BAA6B,CAAC;IAE3C,KAAK,UAAU,MAAM;QACnB,OAAO,OAAO,CAAC,GAAG,IAAI,SAAS,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO;QACL,MAAM;QACN,KAAK,CAAC,MAAM;YACV,OAAO,KAAK,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;gBACpC,MAAM,GAAG,GAAG,MAAM,MAAM,EAAE,CAAC;gBAC3B,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;gBACzD,OAAO,GAAG,GAAG,CAAC,MAAM,YAAY,IAAI,OAAO,CAAC;YAC9C,CAAC,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,MAAM;YACV,OAAO,WAAW,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;gBAC1C,MAAM,GAAG,GAAG,MAAM,MAAM,EAAE,CAAC;gBAC3B,OAAO;oBACL,QAAQ,EAAE,cAAc;oBACxB,GAAG,EAAE,CAAC,yBAAyB,GAAG,CAAC,MAAM,SAAS,CAAC;oBACnD,KAAK,EAAE,CAAC,kEAAkE,CAAC;iBAC5E,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,66 @@
1
+ import { type ServiceAccess, type ServiceStatus } from "../../services/types.ts";
2
+ export interface VercelProject {
3
+ id: string;
4
+ name: string;
5
+ }
6
+ export type Target = "production" | "preview" | "development";
7
+ /**
8
+ * Where one variable applies.
9
+ *
10
+ * A custom environment is not a fourth target — Vercel addresses it by id, in
11
+ * a different field — so the two cannot be expressed as one list.
12
+ */
13
+ export type Scope = {
14
+ target: Target;
15
+ } | {
16
+ customEnvironmentId: string;
17
+ };
18
+ export interface CustomEnvironment {
19
+ id: string;
20
+ slug: string;
21
+ }
22
+ export interface EnvVar {
23
+ id?: string;
24
+ key: string;
25
+ target?: Target[];
26
+ customEnvironmentIds?: string[];
27
+ }
28
+ export declare function vercel(token: string): {
29
+ projects: () => Promise<VercelProject[]>;
30
+ findProject: (app: string) => Promise<VercelProject | undefined>;
31
+ teamId: () => Promise<string | undefined>;
32
+ createProject: (name: string, opts?: {
33
+ repo?: string;
34
+ framework?: string | null;
35
+ }) => Promise<VercelProject>;
36
+ projectDomains: (projectId: string, teamId?: string) => Promise<{
37
+ name: string;
38
+ verified: boolean;
39
+ }[]>;
40
+ addDomain: (projectId: string, domain: string, teamId?: string) => Promise<{
41
+ name: string;
42
+ verified: boolean;
43
+ }>;
44
+ deploy: (name: string, opts: {
45
+ repo: string;
46
+ ref: string;
47
+ target?: "production" | "staging";
48
+ teamId?: string;
49
+ }) => Promise<{
50
+ id: string;
51
+ url: string;
52
+ readyState: string;
53
+ }>;
54
+ promote: (projectId: string, deploymentId: string, teamId?: string) => Promise<unknown>;
55
+ envVars: (projectId: string, teamId?: string) => Promise<EnvVar[]>;
56
+ customEnvironments: (projectId: string, teamId?: string) => Promise<CustomEnvironment[]>;
57
+ ensureCustomEnvironment: (projectId: string, slug: string, teamId?: string) => Promise<CustomEnvironment>;
58
+ setEnv: (projectId: string, key: string, value: string, scope: Scope, opts?: {
59
+ teamId?: string;
60
+ sensitive?: boolean;
61
+ }) => Promise<void>;
62
+ deployments: (projectId?: string, limit?: number) => Promise<unknown[]>;
63
+ status(): Promise<ServiceStatus>;
64
+ access(): Promise<ServiceAccess>;
65
+ };
66
+ //# sourceMappingURL=vercel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vercel.d.ts","sourceRoot":"","sources":["../../../src/admin/services/vercel.ts"],"names":[],"mappings":"AASA,OAAO,EAAsB,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAIrG,MAAM,WAAW,aAAa;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AAC3D,MAAM,MAAM,MAAM,GAAG,YAAY,GAAG,SAAS,GAAG,aAAa,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,MAAM,KAAK,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,mBAAmB,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzE,MAAM,WAAW,iBAAiB;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AAE/D,MAAM,WAAW,MAAM;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM;;uBAeF,MAAM,KAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;kBAYlD,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;0BA8B5C,MAAM,SACN;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,KACjD,OAAO,CAAC,aAAa,CAAC;gCAsEgB,MAAM,WAAW,MAAM;cAC5B,MAAM;kBAAY,OAAO;;2BAKzB,MAAM,UAAU,MAAM,WAAW,MAAM;cALvC,MAAM;kBAAY,OAAO;;mBAsBrD,MAAM,QACN;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;YAG5D,MAAM;aAAO,MAAM;oBAAc,MAAM;;yBAclC,MAAM,gBAAgB,MAAM,WAAW,MAAM;yBAjF7C,MAAM,WAAW,MAAM;oCAuFZ,MAAM,WAAW,MAAM;yCAYvD,MAAM,QAAQ,MAAM,WAAW,MAAM,KAC/C,OAAO,CAAC,iBAAiB,CAAC;wBAtFhB,MAAM,OAAO,MAAM,SAAS,MAAM,SAAS,KAAK,SACrD;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,KAC7C,OAAO,CAAC,IAAI,CAAC;8BA0GkB,MAAM;cAItB,OAAO,CAAC,aAAa,CAAC;cAOtB,OAAO,CAAC,aAAa,CAAC;EA0BzC"}