@swarmclawai/swarmclaw 0.4.0 → 0.4.5

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 (144) hide show
  1. package/README.md +13 -2
  2. package/next.config.ts +8 -0
  3. package/package.json +2 -1
  4. package/src/app/api/agents/[id]/route.ts +20 -21
  5. package/src/app/api/agents/[id]/thread/route.ts +2 -2
  6. package/src/app/api/agents/route.ts +3 -2
  7. package/src/app/api/clawhub/install/route.ts +2 -2
  8. package/src/app/api/connectors/[id]/route.ts +10 -3
  9. package/src/app/api/connectors/[id]/webhook/route.ts +99 -0
  10. package/src/app/api/connectors/route.ts +6 -3
  11. package/src/app/api/credentials/[id]/route.ts +2 -1
  12. package/src/app/api/credentials/route.ts +2 -2
  13. package/src/app/api/documents/route.ts +2 -2
  14. package/src/app/api/files/serve/route.ts +8 -0
  15. package/src/app/api/knowledge/[id]/route.ts +5 -4
  16. package/src/app/api/knowledge/upload/route.ts +2 -2
  17. package/src/app/api/mcp-servers/[id]/route.ts +11 -14
  18. package/src/app/api/mcp-servers/[id]/test/route.ts +2 -1
  19. package/src/app/api/mcp-servers/[id]/tools/route.ts +2 -1
  20. package/src/app/api/mcp-servers/route.ts +2 -2
  21. package/src/app/api/memory/[id]/route.ts +9 -8
  22. package/src/app/api/memory/route.ts +2 -2
  23. package/src/app/api/memory-images/[filename]/route.ts +2 -1
  24. package/src/app/api/openclaw/directory/route.ts +26 -0
  25. package/src/app/api/openclaw/discover/route.ts +61 -0
  26. package/src/app/api/openclaw/sync/route.ts +30 -0
  27. package/src/app/api/orchestrator/run/route.ts +2 -2
  28. package/src/app/api/projects/[id]/route.ts +55 -0
  29. package/src/app/api/projects/route.ts +27 -0
  30. package/src/app/api/providers/[id]/models/route.ts +2 -1
  31. package/src/app/api/providers/[id]/route.ts +13 -15
  32. package/src/app/api/providers/route.ts +2 -2
  33. package/src/app/api/schedules/[id]/route.ts +16 -18
  34. package/src/app/api/schedules/[id]/run/route.ts +4 -3
  35. package/src/app/api/schedules/route.ts +2 -2
  36. package/src/app/api/secrets/[id]/route.ts +16 -17
  37. package/src/app/api/secrets/route.ts +2 -2
  38. package/src/app/api/sessions/[id]/clear/route.ts +2 -1
  39. package/src/app/api/sessions/[id]/deploy/route.ts +2 -1
  40. package/src/app/api/sessions/[id]/devserver/route.ts +2 -1
  41. package/src/app/api/sessions/[id]/messages/route.ts +2 -1
  42. package/src/app/api/sessions/[id]/retry/route.ts +2 -1
  43. package/src/app/api/sessions/[id]/route.ts +2 -1
  44. package/src/app/api/sessions/route.ts +2 -2
  45. package/src/app/api/skills/[id]/route.ts +23 -21
  46. package/src/app/api/skills/import/route.ts +2 -2
  47. package/src/app/api/skills/route.ts +2 -2
  48. package/src/app/api/tasks/[id]/approve/route.ts +2 -1
  49. package/src/app/api/tasks/[id]/route.ts +6 -5
  50. package/src/app/api/tasks/route.ts +2 -2
  51. package/src/app/api/tts/stream/route.ts +48 -0
  52. package/src/app/api/upload/route.ts +2 -2
  53. package/src/app/api/uploads/[filename]/route.ts +4 -1
  54. package/src/app/api/webhooks/[id]/route.ts +29 -31
  55. package/src/app/api/webhooks/route.ts +2 -2
  56. package/src/app/page.tsx +3 -24
  57. package/src/cli/index.js +28 -0
  58. package/src/cli/index.ts +1 -1
  59. package/src/cli/spec.js +2 -0
  60. package/src/components/agents/agent-list.tsx +3 -1
  61. package/src/components/agents/agent-sheet.tsx +116 -14
  62. package/src/components/chat/chat-area.tsx +27 -4
  63. package/src/components/chat/chat-header.tsx +141 -29
  64. package/src/components/chat/tool-call-bubble.tsx +9 -3
  65. package/src/components/chat/voice-overlay.tsx +80 -0
  66. package/src/components/connectors/connector-list.tsx +6 -2
  67. package/src/components/connectors/connector-sheet.tsx +31 -7
  68. package/src/components/layout/app-layout.tsx +47 -25
  69. package/src/components/projects/project-list.tsx +122 -0
  70. package/src/components/projects/project-sheet.tsx +135 -0
  71. package/src/components/schedules/schedule-list.tsx +3 -1
  72. package/src/components/sessions/new-session-sheet.tsx +6 -6
  73. package/src/components/sessions/session-card.tsx +1 -1
  74. package/src/components/sessions/session-list.tsx +7 -7
  75. package/src/components/shared/connector-platform-icon.tsx +4 -0
  76. package/src/components/shared/settings/section-heartbeat.tsx +1 -1
  77. package/src/components/shared/settings/section-orchestrator.tsx +1 -2
  78. package/src/components/shared/settings/section-web-search.tsx +56 -0
  79. package/src/components/shared/settings/settings-page.tsx +73 -0
  80. package/src/components/skills/skill-list.tsx +2 -1
  81. package/src/components/tasks/task-list.tsx +5 -2
  82. package/src/hooks/use-continuous-speech.ts +144 -0
  83. package/src/hooks/use-view-router.ts +52 -0
  84. package/src/hooks/use-voice-conversation.ts +80 -0
  85. package/src/lib/id.ts +6 -0
  86. package/src/lib/projects.ts +13 -0
  87. package/src/lib/provider-sets.ts +5 -0
  88. package/src/lib/providers/anthropic.ts +14 -1
  89. package/src/lib/providers/index.ts +6 -0
  90. package/src/lib/providers/ollama.ts +9 -1
  91. package/src/lib/providers/openai.ts +9 -1
  92. package/src/lib/providers/openclaw.ts +11 -0
  93. package/src/lib/server/api-routes.test.ts +5 -6
  94. package/src/lib/server/build-llm.ts +17 -4
  95. package/src/lib/server/chat-execution.ts +38 -4
  96. package/src/lib/server/collection-helpers.ts +54 -0
  97. package/src/lib/server/connectors/bluebubbles.test.ts +208 -0
  98. package/src/lib/server/connectors/bluebubbles.ts +357 -0
  99. package/src/lib/server/connectors/connector-routing.test.ts +1 -1
  100. package/src/lib/server/connectors/googlechat.ts +46 -7
  101. package/src/lib/server/connectors/manager.ts +392 -3
  102. package/src/lib/server/connectors/media.ts +2 -2
  103. package/src/lib/server/connectors/openclaw.ts +64 -0
  104. package/src/lib/server/connectors/pairing.test.ts +99 -0
  105. package/src/lib/server/connectors/pairing.ts +256 -0
  106. package/src/lib/server/connectors/signal.ts +1 -0
  107. package/src/lib/server/connectors/teams.ts +5 -5
  108. package/src/lib/server/connectors/types.ts +10 -0
  109. package/src/lib/server/execution-log.ts +3 -3
  110. package/src/lib/server/heartbeat-service.ts +1 -1
  111. package/src/lib/server/knowledge-db.test.ts +2 -33
  112. package/src/lib/server/main-agent-loop.ts +6 -6
  113. package/src/lib/server/memory-db.ts +6 -6
  114. package/src/lib/server/openclaw-approvals.ts +105 -0
  115. package/src/lib/server/openclaw-sync.ts +496 -0
  116. package/src/lib/server/orchestrator-lg.ts +30 -9
  117. package/src/lib/server/orchestrator.ts +4 -4
  118. package/src/lib/server/process-manager.ts +2 -2
  119. package/src/lib/server/queue.ts +22 -10
  120. package/src/lib/server/scheduler.ts +2 -2
  121. package/src/lib/server/session-mailbox.ts +2 -2
  122. package/src/lib/server/session-run-manager.ts +2 -2
  123. package/src/lib/server/session-tools/connector.ts +51 -4
  124. package/src/lib/server/session-tools/crud.ts +3 -3
  125. package/src/lib/server/session-tools/delegate.ts +3 -3
  126. package/src/lib/server/session-tools/file.ts +176 -3
  127. package/src/lib/server/session-tools/index.ts +2 -0
  128. package/src/lib/server/session-tools/memory.ts +2 -2
  129. package/src/lib/server/session-tools/openclaw-nodes.ts +112 -0
  130. package/src/lib/server/session-tools/sandbox.ts +33 -0
  131. package/src/lib/server/session-tools/search-providers.ts +270 -0
  132. package/src/lib/server/session-tools/session-info.ts +2 -2
  133. package/src/lib/server/session-tools/web.ts +47 -66
  134. package/src/lib/server/storage.ts +12 -0
  135. package/src/lib/server/stream-agent-chat.ts +29 -0
  136. package/src/lib/server/task-result.test.ts +44 -0
  137. package/src/lib/server/task-result.ts +14 -0
  138. package/src/lib/tool-definitions.ts +5 -3
  139. package/src/lib/tts-stream.ts +130 -0
  140. package/src/lib/view-routes.ts +28 -0
  141. package/src/proxy.ts +3 -0
  142. package/src/stores/use-app-store.ts +28 -1
  143. package/src/stores/use-chat-store.ts +9 -1
  144. package/src/types/index.ts +27 -2
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Streaming TTS utilities: sentence accumulation and ordered audio playback.
3
+ */
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // SentenceAccumulator — buffers text deltas, emits on sentence boundaries
7
+ // ---------------------------------------------------------------------------
8
+
9
+ export class SentenceAccumulator {
10
+ private buffer = ''
11
+ private onSentence: (sentence: string) => void
12
+
13
+ constructor(onSentence: (sentence: string) => void) {
14
+ this.onSentence = onSentence
15
+ }
16
+
17
+ push(delta: string) {
18
+ this.buffer += delta
19
+ // Emit on sentence-ending punctuation followed by space or newline
20
+ const sentenceEnd = /([.!?])\s+/g
21
+ let match: RegExpExecArray | null
22
+ let lastIndex = 0
23
+ while ((match = sentenceEnd.exec(this.buffer)) !== null) {
24
+ const sentence = this.buffer.slice(lastIndex, match.index + 1).trim()
25
+ if (sentence) this.onSentence(sentence)
26
+ lastIndex = match.index + match[0].length
27
+ }
28
+ // Also emit on double newlines
29
+ const doubleNewline = this.buffer.indexOf('\n\n', lastIndex)
30
+ if (doubleNewline !== -1) {
31
+ const sentence = this.buffer.slice(lastIndex, doubleNewline).trim()
32
+ if (sentence) this.onSentence(sentence)
33
+ lastIndex = doubleNewline + 2
34
+ }
35
+ // Flush if buffer exceeds 200 chars without a break
36
+ if (this.buffer.length - lastIndex > 200) {
37
+ const sentence = this.buffer.slice(lastIndex).trim()
38
+ if (sentence) this.onSentence(sentence)
39
+ lastIndex = this.buffer.length
40
+ }
41
+ this.buffer = this.buffer.slice(lastIndex)
42
+ }
43
+
44
+ flush() {
45
+ const remaining = this.buffer.trim()
46
+ if (remaining) this.onSentence(remaining)
47
+ this.buffer = ''
48
+ }
49
+ }
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // AudioChunkQueue — ordered sequential playback of audio chunks
53
+ // ---------------------------------------------------------------------------
54
+
55
+ export class AudioChunkQueue {
56
+ private queue: Promise<ArrayBuffer>[] = []
57
+ private playing = false
58
+ private audioCtx: AudioContext | null = null
59
+ private currentSource: AudioBufferSourceNode | null = null
60
+ private stopped = false
61
+ onComplete?: () => void
62
+
63
+ enqueue(fetchPromise: Promise<ArrayBuffer>) {
64
+ this.queue.push(fetchPromise)
65
+ if (!this.playing) this.playNext()
66
+ }
67
+
68
+ private async playNext() {
69
+ if (this.stopped) return
70
+ if (this.queue.length === 0) {
71
+ this.playing = false
72
+ this.onComplete?.()
73
+ return
74
+ }
75
+
76
+ this.playing = true
77
+ const bufferPromise = this.queue.shift()!
78
+ try {
79
+ if (!this.audioCtx) this.audioCtx = new AudioContext()
80
+ if (this.audioCtx.state === 'suspended') await this.audioCtx.resume()
81
+
82
+ const arrayBuffer = await bufferPromise
83
+ if (this.stopped) return
84
+ const audioBuffer = await this.audioCtx.decodeAudioData(arrayBuffer)
85
+ if (this.stopped) return
86
+
87
+ const source = this.audioCtx.createBufferSource()
88
+ source.buffer = audioBuffer
89
+ source.connect(this.audioCtx.destination)
90
+ this.currentSource = source
91
+
92
+ await new Promise<void>((resolve) => {
93
+ source.onended = () => {
94
+ this.currentSource = null
95
+ resolve()
96
+ }
97
+ source.start()
98
+ })
99
+ } catch {
100
+ // Skip failed chunks
101
+ }
102
+
103
+ if (!this.stopped) this.playNext()
104
+ }
105
+
106
+ stop() {
107
+ this.stopped = true
108
+ this.queue = []
109
+ if (this.currentSource) {
110
+ try { this.currentSource.stop() } catch { /* noop */ }
111
+ this.currentSource = null
112
+ }
113
+ this.playing = false
114
+ }
115
+ }
116
+
117
+ // ---------------------------------------------------------------------------
118
+ // Helper to fetch streaming TTS audio
119
+ // ---------------------------------------------------------------------------
120
+
121
+ export function fetchStreamTts(text: string): Promise<ArrayBuffer> {
122
+ return fetch('/api/tts/stream', {
123
+ method: 'POST',
124
+ headers: { 'Content-Type': 'application/json' },
125
+ body: JSON.stringify({ text }),
126
+ }).then((res) => {
127
+ if (!res.ok) throw new Error(`TTS error: ${res.status}`)
128
+ return res.arrayBuffer()
129
+ })
130
+ }
@@ -0,0 +1,28 @@
1
+ import type { AppView } from '@/types'
2
+
3
+ export const DEFAULT_VIEW: AppView = 'agents'
4
+
5
+ export const VIEW_TO_PATH: Record<AppView, string> = {
6
+ agents: '/agents',
7
+ schedules: '/schedules',
8
+ memory: '/memory',
9
+ tasks: '/tasks',
10
+ secrets: '/secrets',
11
+ providers: '/providers',
12
+ skills: '/skills',
13
+ connectors: '/connectors',
14
+ webhooks: '/webhooks',
15
+ mcp_servers: '/mcp-servers',
16
+ knowledge: '/knowledge',
17
+ plugins: '/plugins',
18
+ usage: '/usage',
19
+ runs: '/runs',
20
+ logs: '/logs',
21
+ settings: '/settings',
22
+ projects: '/projects',
23
+ }
24
+
25
+ const entries = Object.entries(VIEW_TO_PATH) as [AppView, string][]
26
+ export const PATH_TO_VIEW: Record<string, AppView> = Object.fromEntries(
27
+ entries.map(([view, path]) => [path, view]),
28
+ ) as Record<string, AppView>
package/src/proxy.ts CHANGED
@@ -9,6 +9,8 @@ export function proxy(request: NextRequest) {
9
9
  const { pathname } = request.nextUrl
10
10
  const isWebhookTrigger = request.method === 'POST'
11
11
  && /^\/api\/webhooks\/[^/]+\/?$/.test(pathname)
12
+ const isConnectorWebhook = request.method === 'POST'
13
+ && /^\/api\/connectors\/[^/]+\/webhook\/?$/.test(pathname)
12
14
 
13
15
  // Only protect API routes (not auth, uploads served as static assets, or inbound webhooks)
14
16
  if (
@@ -16,6 +18,7 @@ export function proxy(request: NextRequest) {
16
18
  || pathname === '/api/auth'
17
19
  || pathname.startsWith('/api/uploads/')
18
20
  || isWebhookTrigger
21
+ || isConnectorWebhook
19
22
  ) {
20
23
  return NextResponse.next()
21
24
  }
@@ -1,7 +1,7 @@
1
1
  'use client'
2
2
 
3
3
  import { create } from 'zustand'
4
- import type { Sessions, Session, NetworkInfo, Directory, ProviderInfo, Credentials, Agent, Schedule, AppView, BoardTask, AppSettings, OrchestratorSecret, ProviderConfig, Skill, Connector, Webhook, McpServerConfig, PluginMeta } from '../types'
4
+ import type { Sessions, Session, NetworkInfo, Directory, ProviderInfo, Credentials, Agent, Schedule, AppView, BoardTask, AppSettings, OrchestratorSecret, ProviderConfig, Skill, Connector, Webhook, McpServerConfig, PluginMeta, Project } from '../types'
5
5
  import { fetchSessions, fetchDirs, fetchProviders, fetchCredentials } from '../lib/sessions'
6
6
  import { fetchAgents } from '../lib/agents'
7
7
  import { fetchSchedules } from '../lib/schedules'
@@ -151,6 +151,16 @@ interface AppState {
151
151
  editingPluginFilename: string | null
152
152
  setEditingPluginFilename: (filename: string | null) => void
153
153
 
154
+ // Projects
155
+ projects: Record<string, Project>
156
+ loadProjects: () => Promise<void>
157
+ projectSheetOpen: boolean
158
+ setProjectSheetOpen: (open: boolean) => void
159
+ editingProjectId: string | null
160
+ setEditingProjectId: (id: string | null) => void
161
+ activeProjectFilter: string | null
162
+ setActiveProjectFilter: (id: string | null) => void
163
+
154
164
  }
155
165
 
156
166
  export const useAppStore = create<AppState>((set, get) => ({
@@ -465,4 +475,21 @@ export const useAppStore = create<AppState>((set, get) => ({
465
475
  editingPluginFilename: null,
466
476
  setEditingPluginFilename: (filename) => set({ editingPluginFilename: filename }),
467
477
 
478
+ // Projects
479
+ projects: {},
480
+ loadProjects: async () => {
481
+ try {
482
+ const projects = await api<Record<string, Project>>('GET', '/projects')
483
+ set({ projects })
484
+ } catch {
485
+ // ignore
486
+ }
487
+ },
488
+ projectSheetOpen: false,
489
+ setProjectSheetOpen: (open) => set({ projectSheetOpen: open }),
490
+ editingProjectId: null,
491
+ setEditingProjectId: (id) => set({ editingProjectId: id }),
492
+ activeProjectFilter: null,
493
+ setActiveProjectFilter: (id) => set({ activeProjectFilter: id }),
494
+
468
495
  }))
@@ -64,6 +64,10 @@ interface ChatState {
64
64
  retryLastMessage: () => Promise<void>
65
65
  sendHeartbeat: (sessionId: string) => Promise<void>
66
66
  stopStreaming: () => void
67
+
68
+ // Voice conversation
69
+ voiceConversationActive: boolean
70
+ onStreamEvent: ((event: { t: string; text?: string }) => void) | null
67
71
  }
68
72
 
69
73
  export const useChatStore = create<ChatState>((set, get) => ({
@@ -77,6 +81,8 @@ export const useChatStore = create<ChatState>((set, get) => ({
77
81
  lastUsage: null,
78
82
  ttsEnabled: false,
79
83
  toggleTts: () => set((s) => ({ ttsEnabled: !s.ttsEnabled })),
84
+ voiceConversationActive: false,
85
+ onStreamEvent: null,
80
86
 
81
87
  pendingFiles: [],
82
88
  addPendingFile: (f) => set((s) => ({ pendingFiles: [...s.pendingFiles, f] })),
@@ -135,6 +141,8 @@ export const useChatStore = create<ChatState>((set, get) => ({
135
141
  /cancelled by steer mode|stopped by user/i.test(msg || '')
136
142
 
137
143
  await streamChat(sessionId, text, imagePath, imageUrl, (event: SSEEvent) => {
144
+ // Forward events to voice conversation handler if active
145
+ get().onStreamEvent?.(event)
138
146
  if (event.t === 'd') {
139
147
  fullText += event.text || ''
140
148
  set({ streamText: fullText })
@@ -209,7 +217,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
209
217
  streamingSessionId: null,
210
218
  streamText: '',
211
219
  }))
212
- if (get().ttsEnabled) speak(fullText)
220
+ if (get().ttsEnabled && !get().voiceConversationActive) speak(fullText)
213
221
  } else {
214
222
  set({ streaming: false, streamingSessionId: null, streamText: '' })
215
223
  }
@@ -244,6 +244,10 @@ export interface Agent {
244
244
  heartbeatShowOk?: boolean | null
245
245
  heartbeatShowAlerts?: boolean | null
246
246
  heartbeatTarget?: 'last' | 'none' | string | null
247
+ heartbeatGoal?: string | null
248
+ heartbeatNextAction?: string | null
249
+ thinkingLevel?: 'minimal' | 'low' | 'medium' | 'high'
250
+ projectId?: string
247
251
  createdAt: number
248
252
  updatedAt: number
249
253
  }
@@ -263,6 +267,7 @@ export interface Schedule {
263
267
  id: string
264
268
  name: string
265
269
  agentId: string
270
+ projectId?: string
266
271
  taskPrompt: string
267
272
  scheduleType: ScheduleType
268
273
  cron?: string
@@ -323,7 +328,16 @@ export interface MemoryEntry {
323
328
  }
324
329
 
325
330
  export type SessionType = 'human' | 'orchestrated'
326
- export type AppView = 'agents' | 'schedules' | 'memory' | 'tasks' | 'secrets' | 'providers' | 'skills' | 'connectors' | 'webhooks' | 'mcp_servers' | 'knowledge' | 'plugins' | 'usage' | 'runs' | 'logs'
331
+ export type AppView = 'agents' | 'schedules' | 'memory' | 'tasks' | 'secrets' | 'providers' | 'skills' | 'connectors' | 'webhooks' | 'mcp_servers' | 'knowledge' | 'plugins' | 'usage' | 'runs' | 'logs' | 'settings' | 'projects'
332
+
333
+ export interface Project {
334
+ id: string
335
+ name: string
336
+ description: string
337
+ color?: string
338
+ createdAt: number
339
+ updatedAt: number
340
+ }
327
341
 
328
342
  // --- Session Runs ---
329
343
 
@@ -434,6 +448,15 @@ export interface AppSettings {
434
448
  maxLinkedMemoriesExpanded?: number
435
449
  memoryMaxDepth?: number
436
450
  memoryMaxPerLookup?: number
451
+ // Voice conversation
452
+ voiceAutoSendDelaySec?: number
453
+ // Web search provider
454
+ webSearchProvider?: 'duckduckgo' | 'google' | 'bing' | 'searxng' | 'tavily' | 'brave'
455
+ searxngUrl?: string
456
+ // OpenClaw sync settings
457
+ openclawWorkspacePath?: string | null
458
+ openclawAutoSyncMemory?: boolean
459
+ openclawAutoSyncSchedules?: boolean
437
460
  }
438
461
 
439
462
  // --- Orchestrator Secrets ---
@@ -483,6 +506,7 @@ export interface Skill {
483
506
  name: string
484
507
  filename: string
485
508
  content: string
509
+ projectId?: string
486
510
  description?: string
487
511
  sourceUrl?: string
488
512
  sourceFormat?: 'openclaw' | 'plain'
@@ -492,7 +516,7 @@ export interface Skill {
492
516
 
493
517
  // --- Connectors (Chat Platform Bridges) ---
494
518
 
495
- export type ConnectorPlatform = 'discord' | 'telegram' | 'slack' | 'whatsapp' | 'openclaw' | 'signal' | 'teams' | 'googlechat' | 'matrix'
519
+ export type ConnectorPlatform = 'discord' | 'telegram' | 'slack' | 'whatsapp' | 'openclaw' | 'bluebubbles' | 'signal' | 'teams' | 'googlechat' | 'matrix'
496
520
  export type ConnectorStatus = 'stopped' | 'running' | 'error'
497
521
 
498
522
  export interface Connector {
@@ -546,6 +570,7 @@ export interface BoardTask {
546
570
  description: string
547
571
  status: BoardTaskStatus
548
572
  agentId: string
573
+ projectId?: string
549
574
  goalContract?: GoalContract | null
550
575
  cwd?: string | null
551
576
  file?: string | null