causly-server 0.0.0-stage → 1.5.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 (91) hide show
  1. package/.env.example +120 -0
  2. package/CHANGELOG.md +121 -0
  3. package/LICENSE +21 -0
  4. package/README.md +205 -3
  5. package/bin/causly.js +192 -0
  6. package/index.js +229 -0
  7. package/package.json +59 -6
  8. package/packs/calendar.js +141 -0
  9. package/packs/command.js +22 -0
  10. package/packs/database.js +95 -0
  11. package/packs/directory.js +63 -0
  12. package/packs/discord.js +63 -0
  13. package/packs/docker.js +202 -0
  14. package/packs/files.js +106 -0
  15. package/packs/git.js +211 -0
  16. package/packs/github.js +321 -0
  17. package/packs/gmail.js +98 -0
  18. package/packs/leads.js +46 -0
  19. package/packs/linear.js +104 -0
  20. package/packs/loader.js +247 -0
  21. package/packs/manifest.json +1152 -0
  22. package/packs/meetings.js +141 -0
  23. package/packs/notion.js +185 -0
  24. package/packs/pipeline.js +151 -0
  25. package/packs/project.js +70 -0
  26. package/packs/registry.js +149 -0
  27. package/packs/reminders.js +57 -0
  28. package/packs/research.js +34 -0
  29. package/packs/secrets.js +77 -0
  30. package/packs/sentry.js +97 -0
  31. package/packs/seo.js +70 -0
  32. package/packs/slack.js +95 -0
  33. package/packs/stripe.js +127 -0
  34. package/packs/supabase.js +73 -0
  35. package/packs/telegram.js +70 -0
  36. package/packs/terraform.js +247 -0
  37. package/packs/vercel.js +114 -0
  38. package/packs/workflow.js +84 -0
  39. package/prompts/cofounder.js +66 -0
  40. package/prompts/playbooks.js +75 -0
  41. package/tools/backupOps.js +87 -0
  42. package/tools/boundaryAttest.js +184 -0
  43. package/tools/calendarOps.js +563 -0
  44. package/tools/cliOps.js +233 -0
  45. package/tools/commandOps.js +198 -0
  46. package/tools/dbOps.js +117 -0
  47. package/tools/directoryOps.js +96 -0
  48. package/tools/discordOps.js +133 -0
  49. package/tools/dockerOps.js +259 -0
  50. package/tools/envFile.js +89 -0
  51. package/tools/envLoader.js +85 -0
  52. package/tools/fileOps.js +125 -0
  53. package/tools/gitOps.js +170 -0
  54. package/tools/githubOps.js +357 -0
  55. package/tools/gmailOps.js +221 -0
  56. package/tools/googleAuth.js +51 -0
  57. package/tools/googleLogin.js +230 -0
  58. package/tools/leadOps.js +447 -0
  59. package/tools/linearOps.js +304 -0
  60. package/tools/logger.js +47 -0
  61. package/tools/meetingOps.js +697 -0
  62. package/tools/notionOps.js +215 -0
  63. package/tools/paths.js +36 -0
  64. package/tools/pipelineOps.js +511 -0
  65. package/tools/projectOps.js +192 -0
  66. package/tools/reminderAlerts.js +48 -0
  67. package/tools/reminderOps.js +129 -0
  68. package/tools/scheduler.js +46 -0
  69. package/tools/secretsOps.js +133 -0
  70. package/tools/security.js +488 -0
  71. package/tools/sentryOps.js +130 -0
  72. package/tools/seoOps.js +561 -0
  73. package/tools/services.js +295 -0
  74. package/tools/setupOps.js +245 -0
  75. package/tools/slackOps.js +151 -0
  76. package/tools/statusOps.js +104 -0
  77. package/tools/store.js +111 -0
  78. package/tools/stripeOps.js +299 -0
  79. package/tools/supabaseOps.js +94 -0
  80. package/tools/telegramOps.js +153 -0
  81. package/tools/terminal.js +124 -0
  82. package/tools/terraformOps.js +171 -0
  83. package/tools/toolWrapper.js +60 -0
  84. package/tools/toolsetOps.js +123 -0
  85. package/tools/transcribeOps.js +221 -0
  86. package/tools/updateCheck.js +58 -0
  87. package/tools/vercelOps.js +198 -0
  88. package/tools/verifyBoundaryAttest.js +25 -0
  89. package/tools/webOps.js +474 -0
  90. package/tools/workflowOps.js +371 -0
  91. package/tools/workspace.js +85 -0
@@ -0,0 +1,215 @@
1
+ const NOTION_API = "https://api.notion.com/v1";
2
+ const NOTION_VERSION = "2022-06-28";
3
+
4
+ function getToken() {
5
+ const token = process.env.NOTION_API_KEY;
6
+ if (!token) {
7
+ throw new Error(
8
+ "NOTION_API_KEY not set. Run `npm run setup` to configure it, or add it manually to your .env file: NOTION_API_KEY=secret_..."
9
+ );
10
+ }
11
+ return token;
12
+ }
13
+
14
+ async function notionFetch(path, { method = "GET", body } = {}) {
15
+ const token = getToken();
16
+ const res = await fetch(`${NOTION_API}${path}`, {
17
+ method,
18
+ headers: {
19
+ Authorization: `Bearer ${token}`,
20
+ "Notion-Version": NOTION_VERSION,
21
+ "Content-Type": "application/json",
22
+ },
23
+ body: body ? JSON.stringify(body) : undefined,
24
+ });
25
+
26
+ const data = await res.json();
27
+ if (!res.ok) {
28
+ throw new Error(`Notion API error (${path}): ${data.message || data.code || "unknown_error"}`);
29
+ }
30
+ return data;
31
+ }
32
+
33
+ function simplifyPage(p) {
34
+ return {
35
+ id: p.id,
36
+ url: p.url,
37
+ archived: p.archived,
38
+ created_time: p.created_time,
39
+ last_edited_time: p.last_edited_time,
40
+ parent: p.parent,
41
+ properties: p.properties,
42
+ };
43
+ }
44
+
45
+ /** Search pages and databases across the workspace. */
46
+ export async function notionSearch({ query, filter_type, page_size = 20 }) {
47
+ const body = { page_size };
48
+ if (query) body.query = query;
49
+ if (filter_type) body.filter = { property: "object", value: filter_type };
50
+ const data = await notionFetch("/search", { method: "POST", body });
51
+ return {
52
+ results: data.results.map((r) => ({
53
+ id: r.id,
54
+ object: r.object,
55
+ url: r.url,
56
+ title:
57
+ r.properties?.title?.title?.[0]?.plain_text ||
58
+ r.properties?.Name?.title?.[0]?.plain_text ||
59
+ r.title?.[0]?.plain_text ||
60
+ null,
61
+ })),
62
+ has_more: data.has_more,
63
+ next_cursor: data.next_cursor,
64
+ };
65
+ }
66
+
67
+ /** Get a single page's properties and metadata by page ID. */
68
+ export async function notionGetPage({ page_id }) {
69
+ const data = await notionFetch(`/pages/${page_id}`);
70
+ return simplifyPage(data);
71
+ }
72
+
73
+ /** Create a new page under a parent page or database. HIGH risk — requires confirm: true. */
74
+ export async function notionCreatePage({ parent_id, parent_type = "page_id", properties, children, confirm }) {
75
+ const body = {
76
+ parent: { [parent_type]: parent_id },
77
+ properties,
78
+ };
79
+ if (children) body.children = children;
80
+ const data = await notionFetch("/pages", { method: "POST", body });
81
+ return simplifyPage(data);
82
+ }
83
+
84
+ /** Update an existing page's properties, or archive/restore it. HIGH risk — requires confirm: true. */
85
+ export async function notionUpdatePage({ page_id, properties, archived, confirm }) {
86
+ const body = {};
87
+ if (properties) body.properties = properties;
88
+ if (archived !== undefined) body.archived = archived;
89
+ const data = await notionFetch(`/pages/${page_id}`, { method: "PATCH", body });
90
+ return simplifyPage(data);
91
+ }
92
+
93
+ /** Get a database's schema and metadata by database ID. */
94
+ export async function notionGetDatabase({ database_id }) {
95
+ const data = await notionFetch(`/databases/${database_id}`);
96
+ return {
97
+ id: data.id,
98
+ url: data.url,
99
+ title: data.title?.[0]?.plain_text || null,
100
+ properties: data.properties,
101
+ };
102
+ }
103
+
104
+ /** Query a database's rows with optional filter and sort. */
105
+ export async function notionQueryDatabase({ database_id, filter, sorts, page_size = 20 }) {
106
+ const body = { page_size };
107
+ if (filter) body.filter = filter;
108
+ if (sorts) body.sorts = sorts;
109
+ const data = await notionFetch(`/databases/${database_id}/query`, { method: "POST", body });
110
+ return {
111
+ results: data.results.map(simplifyPage),
112
+ has_more: data.has_more,
113
+ next_cursor: data.next_cursor,
114
+ };
115
+ }
116
+
117
+ /** Create a new database under a parent page. HIGH risk — requires confirm: true. */
118
+ export async function notionCreateDatabase({ parent_page_id, title, properties, confirm }) {
119
+ const body = {
120
+ parent: { page_id: parent_page_id },
121
+ title: [{ type: "text", text: { content: title } }],
122
+ properties,
123
+ };
124
+ const data = await notionFetch("/databases", { method: "POST", body });
125
+ return {
126
+ id: data.id,
127
+ url: data.url,
128
+ title,
129
+ properties: data.properties,
130
+ };
131
+ }
132
+
133
+ /** Append content blocks (text, lists, tables, etc.) to a page or block. HIGH risk — requires confirm: true. */
134
+ export async function notionAppendBlockChildren({ block_id, children, confirm }) {
135
+ const data = await notionFetch(`/blocks/${block_id}/children`, {
136
+ method: "PATCH",
137
+ body: { children },
138
+ });
139
+ return { block_id, appended: data.results.length, results: data.results.map((r) => ({ id: r.id, type: r.type })) };
140
+ }
141
+
142
+ /** Get the child blocks of a page or block. */
143
+ export async function notionGetBlockChildren({ block_id, page_size = 50 }) {
144
+ const data = await notionFetch(`/blocks/${block_id}/children?page_size=${page_size}`);
145
+ return {
146
+ block_id,
147
+ blocks: data.results.map((b) => ({ id: b.id, type: b.type, has_children: b.has_children, content: b[b.type] })),
148
+ has_more: data.has_more,
149
+ next_cursor: data.next_cursor,
150
+ };
151
+ }
152
+
153
+ /** Update an existing block's content. HIGH risk — requires confirm: true. */
154
+ export async function notionUpdateBlock({ block_id, block_data, confirm }) {
155
+ const data = await notionFetch(`/blocks/${block_id}`, { method: "PATCH", body: block_data });
156
+ return { id: data.id, type: data.type, updated: true };
157
+ }
158
+
159
+ /** Delete (archive) a block. HIGH risk — requires confirm: true. */
160
+ export async function notionDeleteBlock({ block_id, confirm }) {
161
+ const data = await notionFetch(`/blocks/${block_id}`, { method: "DELETE" });
162
+ return { id: data.id, archived: true };
163
+ }
164
+
165
+ /** Get comments on a page or block. */
166
+ export async function notionGetComments({ block_id }) {
167
+ const data = await notionFetch(`/comments?block_id=${block_id}`);
168
+ return {
169
+ comments: data.results.map((c) => ({
170
+ id: c.id,
171
+ discussion_id: c.discussion_id,
172
+ created_time: c.created_time,
173
+ text: c.rich_text?.map((t) => t.plain_text).join("") || "",
174
+ created_by: c.created_by?.id,
175
+ })),
176
+ };
177
+ }
178
+
179
+ /** Add a comment to a page, or reply in an existing discussion thread. HIGH risk — requires confirm: true. */
180
+ export async function notionAddComment({ page_id, discussion_id, text, confirm }) {
181
+ const body = { rich_text: [{ text: { content: text } }] };
182
+ if (discussion_id) {
183
+ body.discussion_id = discussion_id;
184
+ } else {
185
+ body.parent = { page_id };
186
+ }
187
+ const data = await notionFetch("/comments", { method: "POST", body });
188
+ return { id: data.id, discussion_id: data.discussion_id, posted: true };
189
+ }
190
+
191
+ /** List all users in the workspace. */
192
+ export async function notionListUsers({ page_size = 50 }) {
193
+ const data = await notionFetch(`/users?page_size=${page_size}`);
194
+ return {
195
+ users: data.results.map((u) => ({
196
+ id: u.id,
197
+ name: u.name,
198
+ type: u.type,
199
+ email: u.person?.email || null,
200
+ })),
201
+ has_more: data.has_more,
202
+ next_cursor: data.next_cursor,
203
+ };
204
+ }
205
+
206
+ /** Get a single workspace user's info by user ID. */
207
+ export async function notionGetUser({ user_id }) {
208
+ const u = await notionFetch(`/users/${user_id}`);
209
+ return {
210
+ id: u.id,
211
+ name: u.name,
212
+ type: u.type,
213
+ email: u.person?.email || null,
214
+ };
215
+ }
package/tools/paths.js ADDED
@@ -0,0 +1,36 @@
1
+ // Where the server keeps things.
2
+ //
3
+ // Running from a git checkout, files stay in the repo folder (as always).
4
+ // Installed from npm, the package folder is a cache that gets replaced on upgrade,
5
+ // so settings, logs and the secrets store live in ~/.causly instead (or CAUSLY_HOME).
6
+ import fs from "fs";
7
+ import os from "os";
8
+ import path from "path";
9
+ import { fileURLToPath } from "url";
10
+
11
+ export const PACKAGE_ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
12
+
13
+ export const isInstalledPackage = (root = PACKAGE_ROOT) => root.split(/[\\/]/).includes("node_modules");
14
+
15
+ export const causlyHome = () => process.env.CAUSLY_HOME || path.join(os.homedir(), ".causly");
16
+
17
+ /** Folder for logs and the secrets store. */
18
+ export const dataDir = () => (isInstalledPackage() ? causlyHome() : PACKAGE_ROOT);
19
+
20
+ /**
21
+ * The file `keys` and `setup` write to: CAUSLY_ENV_FILE if set, a git checkout's own .env if it has one,
22
+ * otherwise ~/.causly/.env.
23
+ */
24
+ export const envWritePath = (root = PACKAGE_ROOT) => {
25
+ if (process.env.CAUSLY_ENV_FILE) return process.env.CAUSLY_ENV_FILE;
26
+ const checkout = path.join(root, ".env");
27
+ if (!isInstalledPackage(root) && fs.existsSync(checkout)) return checkout;
28
+ return path.join(causlyHome(), ".env");
29
+ };
30
+
31
+ /**
32
+ * .env files, most specific first: ~/.causly/.env, then the .env next to the code (a git checkout).
33
+ * CAUSLY_ENV_FILE, when set, is used on its own. Values already in the environment always win.
34
+ */
35
+ export const envFiles = () =>
36
+ process.env.CAUSLY_ENV_FILE ? [process.env.CAUSLY_ENV_FILE] : [path.join(causlyHome(), ".env"), path.join(PACKAGE_ROOT, ".env")];