openbot 0.4.2 → 0.4.3
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/dist/app/cli.js
CHANGED
|
@@ -16,7 +16,7 @@ function checkNodeVersion() {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
checkNodeVersion();
|
|
19
|
-
program.name('openbot').description('OpenBot CLI').version('0.4.
|
|
19
|
+
program.name('openbot').description('OpenBot CLI').version('0.4.3');
|
|
20
20
|
program
|
|
21
21
|
.command('start')
|
|
22
22
|
.description('Start the OpenBot harness')
|
|
@@ -70,6 +70,8 @@ export const delegationPlugin = {
|
|
|
70
70
|
channelId: context.state.channelId,
|
|
71
71
|
threadId: context.state.threadId,
|
|
72
72
|
publicBaseUrl: pluginContext.publicBaseUrl,
|
|
73
|
+
// Child events are re-yielded to the parent harness, which persists them once.
|
|
74
|
+
persistEvents: false,
|
|
73
75
|
onEvent: async (outEvent) => {
|
|
74
76
|
// Enrich events with parent metadata so the UI can track the hierarchy
|
|
75
77
|
const enrichedEvent = {
|
|
@@ -229,6 +229,12 @@ const readJsonFile = async (filePath, fallback) => {
|
|
|
229
229
|
throw e;
|
|
230
230
|
}
|
|
231
231
|
};
|
|
232
|
+
const writeJsonFileAtomically = async (filePath, data) => {
|
|
233
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
234
|
+
const tmp = `${filePath}.tmp`;
|
|
235
|
+
await fs.writeFile(tmp, JSON.stringify(data, null, 2), 'utf-8');
|
|
236
|
+
await fs.rename(tmp, filePath);
|
|
237
|
+
};
|
|
232
238
|
const toVariablesRecord = (raw) => {
|
|
233
239
|
if (!raw || typeof raw !== 'object') {
|
|
234
240
|
return {};
|
|
@@ -408,8 +414,7 @@ export const storageService = {
|
|
|
408
414
|
let displayName = name;
|
|
409
415
|
let participants = [];
|
|
410
416
|
try {
|
|
411
|
-
const
|
|
412
|
-
const parsed = JSON.parse(stateContent);
|
|
417
|
+
const parsed = await readJsonFile(statePath, {});
|
|
413
418
|
const fields = readChannelStateFileFields(parsed);
|
|
414
419
|
cwd = fields.cwd;
|
|
415
420
|
displayName = fields.name ?? name;
|
|
@@ -479,7 +484,7 @@ export const storageService = {
|
|
|
479
484
|
await fs.mkdir(channelDir, { recursive: true });
|
|
480
485
|
await fs.writeFile(specPath, spec?.trim() ||
|
|
481
486
|
`# ${normalizedChannelId}\n\n`);
|
|
482
|
-
await
|
|
487
|
+
await writeJsonFileAtomically(statePath, finalState);
|
|
483
488
|
},
|
|
484
489
|
deleteChannel: async ({ channelId }) => {
|
|
485
490
|
const normalizedChannelId = channelId.trim();
|
|
@@ -540,8 +545,7 @@ export const storageService = {
|
|
|
540
545
|
if (threadTitle?.trim()) {
|
|
541
546
|
baseState.name = threadTitle.trim();
|
|
542
547
|
}
|
|
543
|
-
await
|
|
544
|
-
await fs.writeFile(statePath, JSON.stringify(baseState, null, 2));
|
|
548
|
+
await writeJsonFileAtomically(statePath, baseState);
|
|
545
549
|
},
|
|
546
550
|
getThreads: async ({ channelId }) => {
|
|
547
551
|
const threadsDir = resolvePath(resolveBaseDir() + '/' + DEFAULT_CHANNELS_DIR + '/' + channelId + '/threads');
|
|
@@ -560,8 +564,7 @@ export const storageService = {
|
|
|
560
564
|
const threadStatePath = path.join(threadPath, 'state.json');
|
|
561
565
|
let threadDisplayName = name;
|
|
562
566
|
try {
|
|
563
|
-
const
|
|
564
|
-
const threadState = JSON.parse(threadStateRaw);
|
|
567
|
+
const threadState = await readJsonFile(threadStatePath, {});
|
|
565
568
|
const threadName = typeof threadState.name === 'string' ? threadState.name.trim() : '';
|
|
566
569
|
if (threadName) {
|
|
567
570
|
threadDisplayName = threadName;
|
|
@@ -597,8 +600,7 @@ export const storageService = {
|
|
|
597
600
|
const statePath = `${threadDir}/state.json`;
|
|
598
601
|
let state = {};
|
|
599
602
|
try {
|
|
600
|
-
|
|
601
|
-
state = JSON.parse(stateContent);
|
|
603
|
+
state = await readJsonFile(statePath, {});
|
|
602
604
|
}
|
|
603
605
|
catch (error) {
|
|
604
606
|
if (error?.code !== 'ENOENT') {
|
|
@@ -630,8 +632,7 @@ export const storageService = {
|
|
|
630
632
|
}
|
|
631
633
|
let state = {};
|
|
632
634
|
try {
|
|
633
|
-
|
|
634
|
-
state = JSON.parse(stateContent);
|
|
635
|
+
state = await readJsonFile(statePath, {});
|
|
635
636
|
}
|
|
636
637
|
catch (error) {
|
|
637
638
|
if (error?.code !== 'ENOENT') {
|
|
@@ -662,8 +663,7 @@ export const storageService = {
|
|
|
662
663
|
...currentState,
|
|
663
664
|
...patch,
|
|
664
665
|
};
|
|
665
|
-
await
|
|
666
|
-
await fs.writeFile(statePath, JSON.stringify(newState, null, 2));
|
|
666
|
+
await writeJsonFileAtomically(statePath, newState);
|
|
667
667
|
}
|
|
668
668
|
catch (error) {
|
|
669
669
|
console.error(`Failed to patch channel state for channel ${channelId}`, error);
|
|
@@ -680,8 +680,7 @@ export const storageService = {
|
|
|
680
680
|
...currentState,
|
|
681
681
|
...patch,
|
|
682
682
|
};
|
|
683
|
-
await
|
|
684
|
-
await fs.writeFile(statePath, JSON.stringify(newState, null, 2));
|
|
683
|
+
await writeJsonFileAtomically(statePath, newState);
|
|
685
684
|
}
|
|
686
685
|
catch (error) {
|
|
687
686
|
console.error(`Failed to patch thread state for channel ${channelId} thread ${threadId}`, error);
|
package/package.json
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openbot",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.12.0"
|
|
8
8
|
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "tsx watch src/app/cli.ts start",
|
|
11
|
+
"build": "tsc && mkdir -p dist/assets && cp src/assets/icon.svg dist/assets/icon.svg",
|
|
12
|
+
"start": "node dist/app/cli.js start"
|
|
13
|
+
},
|
|
9
14
|
"bin": {
|
|
10
15
|
"openbot": "./dist/app/cli.js"
|
|
11
16
|
},
|
|
@@ -30,10 +35,5 @@
|
|
|
30
35
|
"@types/node": "^20.10.1",
|
|
31
36
|
"tsx": "^4.21.0",
|
|
32
37
|
"typescript": "^5.9.3"
|
|
33
|
-
},
|
|
34
|
-
"scripts": {
|
|
35
|
-
"dev": "tsx watch src/app/cli.ts start",
|
|
36
|
-
"build": "tsc && mkdir -p dist/assets && cp src/assets/icon.svg dist/assets/icon.svg",
|
|
37
|
-
"start": "node dist/app/cli.js start"
|
|
38
38
|
}
|
|
39
|
-
}
|
|
39
|
+
}
|
package/src/app/cli.ts
CHANGED
|
@@ -85,6 +85,8 @@ export const delegationPlugin: Plugin = {
|
|
|
85
85
|
channelId: context.state.channelId,
|
|
86
86
|
threadId: context.state.threadId,
|
|
87
87
|
publicBaseUrl: pluginContext.publicBaseUrl,
|
|
88
|
+
// Child events are re-yielded to the parent harness, which persists them once.
|
|
89
|
+
persistEvents: false,
|
|
88
90
|
onEvent: async (outEvent) => {
|
|
89
91
|
// Enrich events with parent metadata so the UI can track the hierarchy
|
|
90
92
|
const enrichedEvent = {
|
|
@@ -294,6 +294,13 @@ const readJsonFile = async <T>(filePath: string, fallback: T): Promise<T> => {
|
|
|
294
294
|
}
|
|
295
295
|
};
|
|
296
296
|
|
|
297
|
+
const writeJsonFileAtomically = async (filePath: string, data: unknown): Promise<void> => {
|
|
298
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
299
|
+
const tmp = `${filePath}.tmp`;
|
|
300
|
+
await fs.writeFile(tmp, JSON.stringify(data, null, 2), 'utf-8');
|
|
301
|
+
await fs.rename(tmp, filePath);
|
|
302
|
+
};
|
|
303
|
+
|
|
297
304
|
const toVariablesRecord = (raw: unknown): Record<string, string> => {
|
|
298
305
|
if (!raw || typeof raw !== 'object') {
|
|
299
306
|
return {};
|
|
@@ -502,8 +509,7 @@ export const storageService = {
|
|
|
502
509
|
let participants: string[] = [];
|
|
503
510
|
|
|
504
511
|
try {
|
|
505
|
-
const
|
|
506
|
-
const parsed = JSON.parse(stateContent);
|
|
512
|
+
const parsed = await readJsonFile(statePath, {});
|
|
507
513
|
const fields = readChannelStateFileFields(parsed);
|
|
508
514
|
cwd = fields.cwd;
|
|
509
515
|
displayName = fields.name ?? name;
|
|
@@ -597,7 +603,7 @@ export const storageService = {
|
|
|
597
603
|
spec?.trim() ||
|
|
598
604
|
`# ${normalizedChannelId}\n\n`,
|
|
599
605
|
);
|
|
600
|
-
await
|
|
606
|
+
await writeJsonFileAtomically(statePath, finalState);
|
|
601
607
|
},
|
|
602
608
|
deleteChannel: async ({ channelId }: { channelId: string }): Promise<void> => {
|
|
603
609
|
const normalizedChannelId = channelId.trim();
|
|
@@ -673,8 +679,7 @@ export const storageService = {
|
|
|
673
679
|
baseState.name = threadTitle.trim();
|
|
674
680
|
}
|
|
675
681
|
|
|
676
|
-
await
|
|
677
|
-
await fs.writeFile(statePath, JSON.stringify(baseState, null, 2));
|
|
682
|
+
await writeJsonFileAtomically(statePath, baseState);
|
|
678
683
|
},
|
|
679
684
|
getThreads: async ({ channelId }: { channelId: string }): Promise<Thread[]> => {
|
|
680
685
|
const threadsDir = resolvePath(
|
|
@@ -698,8 +703,7 @@ export const storageService = {
|
|
|
698
703
|
let threadDisplayName = name;
|
|
699
704
|
|
|
700
705
|
try {
|
|
701
|
-
const
|
|
702
|
-
const threadState = JSON.parse(threadStateRaw) as Record<string, unknown>;
|
|
706
|
+
const threadState = await readJsonFile<Record<string, unknown>>(threadStatePath, {});
|
|
703
707
|
const threadName =
|
|
704
708
|
typeof threadState.name === 'string' ? threadState.name.trim() : '';
|
|
705
709
|
if (threadName) {
|
|
@@ -748,8 +752,7 @@ export const storageService = {
|
|
|
748
752
|
|
|
749
753
|
let state: unknown = {};
|
|
750
754
|
try {
|
|
751
|
-
|
|
752
|
-
state = JSON.parse(stateContent);
|
|
755
|
+
state = await readJsonFile(statePath, {});
|
|
753
756
|
} catch (error: unknown) {
|
|
754
757
|
if ((error as NodeJS.ErrnoException)?.code !== 'ENOENT') {
|
|
755
758
|
console.error(
|
|
@@ -787,8 +790,7 @@ export const storageService = {
|
|
|
787
790
|
|
|
788
791
|
let state: unknown = {};
|
|
789
792
|
try {
|
|
790
|
-
|
|
791
|
-
state = JSON.parse(stateContent);
|
|
793
|
+
state = await readJsonFile(statePath, {});
|
|
792
794
|
} catch (error: unknown) {
|
|
793
795
|
if ((error as NodeJS.ErrnoException)?.code !== 'ENOENT') {
|
|
794
796
|
console.error(`Failed to read state file for channel ${channelId}`, error);
|
|
@@ -831,8 +833,7 @@ export const storageService = {
|
|
|
831
833
|
...(patch as Record<string, unknown>),
|
|
832
834
|
};
|
|
833
835
|
|
|
834
|
-
await
|
|
835
|
-
await fs.writeFile(statePath, JSON.stringify(newState, null, 2));
|
|
836
|
+
await writeJsonFileAtomically(statePath, newState);
|
|
836
837
|
} catch (error) {
|
|
837
838
|
console.error(`Failed to patch channel state for channel ${channelId}`, error);
|
|
838
839
|
throw error;
|
|
@@ -859,8 +860,7 @@ export const storageService = {
|
|
|
859
860
|
...(patch as Record<string, unknown>),
|
|
860
861
|
};
|
|
861
862
|
|
|
862
|
-
await
|
|
863
|
-
await fs.writeFile(statePath, JSON.stringify(newState, null, 2));
|
|
863
|
+
await writeJsonFileAtomically(statePath, newState);
|
|
864
864
|
} catch (error) {
|
|
865
865
|
console.error(
|
|
866
866
|
`Failed to patch thread state for channel ${channelId} thread ${threadId}`,
|