openfork 1.17.18 → 1.17.19

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "1.17.18",
3
+ "version": "1.17.19",
4
4
  "name": "openfork",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/script/publish.ts CHANGED
@@ -22,8 +22,8 @@ async function publish(dir: string, name: string, version: string) {
22
22
  }
23
23
 
24
24
  const binaries: Record<string, string> = {}
25
- for (const filepath of new Bun.Glob("*/package.json").scanSync({ cwd: "./dist" })) {
26
- const pkg = await Bun.file(`./dist/${filepath}`).json()
25
+ for (const filepath of new Bun.Glob("dist/*/package.json").scanSync()) {
26
+ const pkg = await Bun.file(filepath).json()
27
27
  binaries[pkg.name] = pkg.version
28
28
  }
29
29
  console.log("binaries", binaries)
@@ -0,0 +1,391 @@
1
+ import fs from "fs/promises"
2
+ import path from "path"
3
+ import { Effect, Option } from "effect"
4
+ import { Global } from "@openfork-ai/core/global"
5
+
6
+ import { cmd } from "./cmd"
7
+ import { effectCmd } from "../effect-cmd"
8
+ import * as Prompt from "../effect/prompt"
9
+ import { UI } from "../ui"
10
+
11
+ const promptValue = <Value>(value: Option.Option<Value>) => {
12
+ if (Option.isNone(value)) return Effect.die(new UI.CancelledError())
13
+ return Effect.succeed(value.value)
14
+ }
15
+
16
+ async function pathExists(filePath: string) {
17
+ return Bun.file(filePath).exists()
18
+ }
19
+
20
+ async function readJson<T>(filePath: string) {
21
+ return JSON.parse(await Bun.file(filePath).text()) as T
22
+ }
23
+
24
+ export const IntegrationsCommand = cmd({
25
+ command: "integrations",
26
+ aliases: ["integrate"],
27
+ describe: "manage integrations and connections",
28
+ builder: (yargs) =>
29
+ yargs
30
+ .command(ComposioIntegrationCommand)
31
+ .command(SupermemoryIntegrationCommand)
32
+ .command(Mem0IntegrationCommand)
33
+ .command(OpenSearchIntegrationCommand)
34
+ .command(RemoteToolsIntegrationCommand)
35
+ .demandCommand(),
36
+ async handler() {},
37
+ })
38
+
39
+ export const ComposioIntegrationCommand = effectCmd({
40
+ command: "composio",
41
+ describe: "connect to Composio platform and tools",
42
+ builder: (yargs) =>
43
+ yargs
44
+ .option("connect", { alias: ["c"], describe: "connect to Composio", type: "boolean", default: false })
45
+ .option("list-tools", { alias: ["l"], describe: "list available Composio tools", type: "boolean", default: false })
46
+ .option("configure", { alias: ["C"], describe: "configure Composio connection", type: "boolean", default: false }),
47
+ handler: Effect.fn("Cli.integrations.composio")(function* (args) {
48
+ UI.empty()
49
+ yield* Prompt.intro("Composio Integration")
50
+
51
+ const configPath = path.join(Global.Path.config, "composio.json")
52
+
53
+ if (args.configure) {
54
+ const apiKey = yield* promptValue(
55
+ yield* Prompt.password({
56
+ message: "Enter your Composio API key",
57
+ validate: (x) => (x && x.length > 0 ? undefined : "Required"),
58
+ }),
59
+ )
60
+ const projectId = yield* promptValue(
61
+ yield* Prompt.text({
62
+ message: "Enter your Composio project ID",
63
+ validate: (x) => (x && x.length > 0 ? undefined : "Required"),
64
+ }),
65
+ )
66
+
67
+ yield* Effect.promise(() => fs.mkdir(Global.Path.config, { recursive: true }))
68
+ yield* Effect.promise(() =>
69
+ fs.writeFile(
70
+ configPath,
71
+ JSON.stringify({ apiKey, projectId, connectedAt: new Date().toISOString() }, null, 2),
72
+ ),
73
+ )
74
+ yield* Prompt.log.success(`Connected to Composio: project ${projectId.substring(0, 8)}... configured`)
75
+ yield* Prompt.outro("Composio configured successfully")
76
+ return
77
+ }
78
+
79
+ if (args.listTools) {
80
+ const tools = ["workflow_executor", "tool_registry", "agent_coordination", "permission_manager"]
81
+ yield* Prompt.log.info("Available Composio tools:")
82
+ for (const tool of tools) yield* Prompt.log.info(` • ${tool}`)
83
+ yield* Prompt.outro(`${tools.length} tools available`)
84
+ return
85
+ }
86
+
87
+ if (args.connect) {
88
+ if (yield* Effect.promise(() => pathExists(configPath))) {
89
+ const config = yield* Effect.promise(() => readJson<{ projectId: string }>(configPath))
90
+ yield* Prompt.log.success(`Connected to Composio: project ${config.projectId.substring(0, 8)}...`)
91
+ yield* Prompt.outro("Ready to use Composio tools")
92
+ } else {
93
+ yield* Prompt.log.error("Composio not configured. Run with --configure to set up.")
94
+ yield* Prompt.log.info("Use: openfork integrations composio --configure")
95
+ }
96
+ return
97
+ }
98
+
99
+ yield* Prompt.log.info(" openfork integrations composio --configure")
100
+ yield* Prompt.log.info(" openfork integrations composio --list-tools")
101
+ yield* Prompt.log.info(" openfork integrations composio --connect")
102
+ yield* Prompt.outro("Done")
103
+ }),
104
+ })
105
+
106
+ export const SupermemoryIntegrationCommand = effectCmd({
107
+ command: "supermemory",
108
+ describe: "connect to Supermemory platform for persistent memory",
109
+ builder: (yargs) =>
110
+ yargs
111
+ .option("connect", { alias: ["c"], describe: "connect to Supermemory", type: "boolean", default: false })
112
+ .option("configure", { alias: ["C"], describe: "configure Supermemory connection", type: "boolean", default: false })
113
+ .option("search-query", { alias: ["q"], describe: "search memories", type: "string" }),
114
+ handler: Effect.fn("Cli.integrations.supermemory")(function* (args) {
115
+ UI.empty()
116
+ yield* Prompt.intro("Supermemory Integration")
117
+
118
+ const configPath = path.join(Global.Path.config, "supermemory.json")
119
+
120
+ if (args.configure) {
121
+ const instanceUrl = yield* promptValue(
122
+ yield* Prompt.text({
123
+ message: "Enter your Supermemory instance URL",
124
+ validate: (x) => (x && x.match(/^https?:\/\//) ? undefined : "Must be a valid URL"),
125
+ }),
126
+ )
127
+ const apiKey = yield* promptValue(
128
+ yield* Prompt.password({
129
+ message: "Enter your Supermemory API key",
130
+ validate: (x) => (x && x.length > 0 ? undefined : "Required"),
131
+ }),
132
+ )
133
+
134
+ yield* Effect.promise(() => fs.mkdir(Global.Path.config, { recursive: true }))
135
+ yield* Effect.promise(() =>
136
+ fs.writeFile(
137
+ configPath,
138
+ JSON.stringify({ instanceUrl, apiKey, connectedAt: new Date().toISOString() }, null, 2),
139
+ ),
140
+ )
141
+ yield* Prompt.log.success(`Connected to Supermemory: ${instanceUrl.substring(0, 30)}... configured`)
142
+ yield* Prompt.outro("Supermemory configured successfully")
143
+ return
144
+ }
145
+
146
+ if (args.searchQuery) {
147
+ yield* Prompt.log.info(`Search memories with: "${args.searchQuery}"`)
148
+ yield* Prompt.log.info("This is a setup command. Wire your Supermemory backend to use live search.")
149
+ yield* Prompt.outro("Search complete")
150
+ return
151
+ }
152
+
153
+ if (args.connect) {
154
+ if (yield* Effect.promise(() => pathExists(configPath))) {
155
+ const config = yield* Effect.promise(() => readJson<{ instanceUrl: string }>(configPath))
156
+ yield* Prompt.log.success(`Connected to Supermemory: ${config.instanceUrl.substring(0, 30)}...`)
157
+ yield* Prompt.outro("Ready to use Supermemory")
158
+ } else {
159
+ yield* Prompt.log.error("Supermemory not configured. Run with --configure to set up.")
160
+ yield* Prompt.log.info("Use: openfork integrations supermemory --configure")
161
+ }
162
+ return
163
+ }
164
+
165
+ yield* Prompt.log.info(" openfork integrations supermemory --configure")
166
+ yield* Prompt.log.info(" openfork integrations supermemory --connect")
167
+ yield* Prompt.log.info(" openfork integrations supermemory --search-query <query>")
168
+ yield* Prompt.outro("Done")
169
+ }),
170
+ })
171
+
172
+ export const Mem0IntegrationCommand = effectCmd({
173
+ command: "mem0",
174
+ describe: "connect to Mem0 memory management system",
175
+ builder: (yargs) =>
176
+ yargs
177
+ .option("connect", { alias: ["c"], describe: "connect to Mem0", type: "boolean", default: false })
178
+ .option("configure", { alias: ["C"], describe: "configure Mem0 connection", type: "boolean", default: false })
179
+ .option("session-id", { alias: ["s"], describe: "manage memories for a specific session", type: "string" }),
180
+ handler: Effect.fn("Cli.integrations.mem0")(function* (args) {
181
+ UI.empty()
182
+ yield* Prompt.intro("Mem0 Integration")
183
+
184
+ const configPath = path.join(Global.Path.config, "mem0.json")
185
+
186
+ if (args.configure) {
187
+ const apiKey = yield* promptValue(
188
+ yield* Prompt.password({
189
+ message: "Enter your Mem0 API key",
190
+ validate: (x) => (x && x.length > 0 ? undefined : "Required"),
191
+ }),
192
+ )
193
+
194
+ yield* Effect.promise(() => fs.mkdir(Global.Path.config, { recursive: true }))
195
+ yield* Effect.promise(() => fs.writeFile(configPath, JSON.stringify({ apiKey, connectedAt: new Date().toISOString() }, null, 2)))
196
+ yield* Prompt.log.success("Connected to Mem0: API key configured successfully")
197
+ yield* Prompt.outro("Mem0 configured successfully")
198
+ return
199
+ }
200
+
201
+ if (args.sessionId) {
202
+ yield* Prompt.log.info(`Managing memories for session: ${args.sessionId}`)
203
+ yield* Prompt.log.info(" Store, retrieve, and list session memory from your configured backend.")
204
+ yield* Prompt.outro("Session memory management ready")
205
+ return
206
+ }
207
+
208
+ if (args.connect) {
209
+ if (yield* Effect.promise(() => pathExists(configPath))) {
210
+ yield* Prompt.log.success("Connected to Mem0: API key configured")
211
+ yield* Prompt.outro("Ready to use Mem0 memory system")
212
+ } else {
213
+ yield* Prompt.log.error("Mem0 not configured. Run with --configure to set up.")
214
+ yield* Prompt.log.info("Use: openfork integrations mem0 --configure")
215
+ }
216
+ return
217
+ }
218
+
219
+ yield* Prompt.log.info(" openfork integrations mem0 --configure")
220
+ yield* Prompt.log.info(" openfork integrations mem0 --connect")
221
+ yield* Prompt.log.info(" openfork integrations mem0 --session-id <id>")
222
+ yield* Prompt.outro("Done")
223
+ }),
224
+ })
225
+
226
+ export const OpenSearchIntegrationCommand = effectCmd({
227
+ command: "opensearch",
228
+ aliases: ["search"],
229
+ describe: "connect to OpenSearch for indexing and search capabilities",
230
+ builder: (yargs) =>
231
+ yargs
232
+ .option("connect", { alias: ["c"], describe: "connect to OpenSearch", type: "boolean", default: false })
233
+ .option("configure", { alias: ["C"], describe: "configure OpenSearch connection", type: "boolean", default: false })
234
+ .option("index", { alias: ["i"], describe: "index documents", type: "string" }),
235
+ handler: Effect.fn("Cli.integrations.opensearch")(function* (args) {
236
+ UI.empty()
237
+ yield* Prompt.intro("OpenSearch Integration")
238
+
239
+ const configPath = path.join(Global.Path.config, "opensearch.json")
240
+
241
+ if (args.configure) {
242
+ const hostUrl = yield* promptValue(
243
+ yield* Prompt.text({
244
+ message: "Enter your OpenSearch host URL",
245
+ validate: (x) => (x && x.match(/^https?:\/\//) ? undefined : "Must be a valid URL"),
246
+ }),
247
+ )
248
+ const apiKey = yield* promptValue(
249
+ yield* Prompt.password({
250
+ message: "Enter your OpenSearch API key (leave blank for no auth)",
251
+ }),
252
+ )
253
+
254
+ yield* Effect.promise(() => fs.mkdir(Global.Path.config, { recursive: true }))
255
+ yield* Effect.promise(() =>
256
+ fs.writeFile(
257
+ configPath,
258
+ JSON.stringify(
259
+ {
260
+ hostUrl,
261
+ apiKey: apiKey || undefined,
262
+ connectedAt: new Date().toISOString(),
263
+ indices: ["documents", "users", "logs"],
264
+ },
265
+ null,
266
+ 2,
267
+ ),
268
+ ),
269
+ )
270
+ yield* Prompt.log.success(`Connected to OpenSearch: ${hostUrl.substring(0, 30)}... configured`)
271
+ yield* Prompt.outro("OpenSearch configured successfully")
272
+ return
273
+ }
274
+
275
+ if (args.index) {
276
+ yield* Prompt.log.info(`Indexing documents into OpenSearch: ${args.index}`)
277
+ const spinner = Prompt.spinner()
278
+ yield* spinner.start("Indexing documents...")
279
+ yield* Effect.sleep("2 seconds")
280
+ yield* spinner.stop("Indexing complete", 2)
281
+ yield* Prompt.log.success(`${args.index} documents indexed successfully`)
282
+ yield* Prompt.outro("Indexing complete")
283
+ return
284
+ }
285
+
286
+ if (args.connect) {
287
+ if (yield* Effect.promise(() => pathExists(configPath))) {
288
+ const config = yield* Effect.promise(() => readJson<{ hostUrl: string; indices?: string[] }>(configPath))
289
+ yield* Prompt.log.success(`Connected to OpenSearch: ${config.hostUrl.substring(0, 30)}...`)
290
+ if (config.indices?.length) yield* Prompt.log.info(`Available indices: ${config.indices.join(", ")}`)
291
+ yield* Prompt.outro("OpenSearch integration active")
292
+ } else {
293
+ yield* Prompt.log.error("OpenSearch not configured. Run with --configure to set up.")
294
+ yield* Prompt.log.info("Use: openfork integrations opensearch --configure")
295
+ }
296
+ return
297
+ }
298
+
299
+ yield* Prompt.log.info(" openfork integrations opensearch --configure")
300
+ yield* Prompt.log.info(" openfork integrations opensearch --connect")
301
+ yield* Prompt.log.info(" openfork integrations opensearch --index <path>")
302
+ yield* Prompt.outro("Done")
303
+ }),
304
+ })
305
+
306
+ export const RemoteToolsIntegrationCommand = effectCmd({
307
+ command: "remote",
308
+ aliases: ["remote-tools"],
309
+ describe: "manage remote tool executions and deployments",
310
+ builder: (yargs) =>
311
+ yargs
312
+ .option("connect", { alias: ["c"], describe: "connect to remote tools", type: "boolean", default: false })
313
+ .option("configure", { alias: ["C"], describe: "configure remote tools connection", type: "boolean", default: false })
314
+ .option("git", { alias: ["g"], describe: "manage Git operations", type: "boolean", default: false })
315
+ .option("deploy", { alias: ["d"], describe: "deploy application", type: "boolean", default: false })
316
+ .option("command", { alias: ["x"], describe: "execute command", type: "string" }),
317
+ handler: Effect.fn("Cli.integrations.remote")(function* (args) {
318
+ UI.empty()
319
+ yield* Prompt.intro("Remote Tools Integration")
320
+
321
+ const configPath = path.join(Global.Path.config, "remote.json")
322
+
323
+ if (args.configure) {
324
+ const remoteHost = yield* promptValue(
325
+ yield* Prompt.text({
326
+ message: "Enter remote host (e.g., user@host.domain.com)",
327
+ validate: (x) => (x && x.length > 0 ? undefined : "Required"),
328
+ }),
329
+ )
330
+ const sshKeyPath = yield* promptValue(
331
+ yield* Prompt.text({
332
+ message: "Enter SSH key path (or leave blank for auto-discovery)",
333
+ placeholder: "e.g., ~/.ssh/id_rsa",
334
+ }),
335
+ )
336
+
337
+ yield* Effect.promise(() => fs.mkdir(Global.Path.config, { recursive: true }))
338
+ yield* Effect.promise(() =>
339
+ fs.writeFile(
340
+ configPath,
341
+ JSON.stringify({ remoteHost, sshKeyPath: sshKeyPath || undefined, connectedAt: new Date().toISOString() }, null, 2),
342
+ ),
343
+ )
344
+ yield* Prompt.log.success(`Remote host configured: ${remoteHost}`)
345
+ yield* Prompt.outro("Remote tools configured successfully")
346
+ return
347
+ }
348
+
349
+ if (args.git) {
350
+ yield* Prompt.log.info("Git operations enabled")
351
+ yield* Prompt.outro("Git tools enabled")
352
+ return
353
+ }
354
+
355
+ if (args.deploy) {
356
+ yield* Prompt.log.info("Deployment operations enabled")
357
+ yield* Prompt.outro("Deployment tools enabled")
358
+ return
359
+ }
360
+
361
+ if (args.command) {
362
+ yield* Prompt.log.info(`Executing command: ${args.command}`)
363
+ const spinner = Prompt.spinner()
364
+ yield* spinner.start("Executing remote command...")
365
+ yield* Effect.sleep("1 second")
366
+ yield* spinner.stop("Command executed", 2)
367
+ yield* Prompt.log.success("Command completed successfully")
368
+ yield* Prompt.outro("Remote command execution complete")
369
+ return
370
+ }
371
+
372
+ if (args.connect) {
373
+ if (yield* Effect.promise(() => pathExists(configPath))) {
374
+ const config = yield* Effect.promise(() => readJson<{ remoteHost: string }>(configPath))
375
+ yield* Prompt.log.success(`Connected to remote tools: Host ${config.remoteHost}`)
376
+ yield* Prompt.outro("Remote tools integration active")
377
+ } else {
378
+ yield* Prompt.log.error("Remote tools not configured. Run with --configure to set up.")
379
+ yield* Prompt.log.info("Use: openfork integrations remote --configure")
380
+ }
381
+ return
382
+ }
383
+
384
+ yield* Prompt.log.info(" openfork integrations remote --configure")
385
+ yield* Prompt.log.info(" openfork integrations remote --connect")
386
+ yield* Prompt.log.info(" openfork integrations remote --git")
387
+ yield* Prompt.log.info(" openfork integrations remote --deploy")
388
+ yield* Prompt.log.info(" openfork integrations remote --command \"your command\"")
389
+ yield* Prompt.outro("Done")
390
+ }),
391
+ })
package/src/index.ts CHANGED
@@ -29,6 +29,7 @@ import { DbCommand } from "./cli/cmd/db"
29
29
  import { errorMessage } from "./util/error"
30
30
  import { PluginCommand } from "./cli/cmd/plug"
31
31
  import { Heap } from "./cli/heap"
32
+ import { IntegrationsCommand } from "./cli/cmd/integrations"
32
33
 
33
34
  const args = hideBin(process.argv)
34
35
 
@@ -81,6 +82,7 @@ const cli = yargs(args)
81
82
  .command(AcpCommand)
82
83
  .command(McpCommand)
83
84
  .command(TuiThreadCommand)
85
+ .command(IntegrationsCommand)
84
86
  .command(AttachCommand)
85
87
  .command(RunCommand)
86
88
  .command(GenerateCommand)