opensoma 0.5.0 → 0.6.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 (133) hide show
  1. package/dist/package.json +5 -1
  2. package/dist/src/agent-browser-launcher.d.ts +43 -0
  3. package/dist/src/agent-browser-launcher.d.ts.map +1 -0
  4. package/dist/src/agent-browser-launcher.js +97 -0
  5. package/dist/src/agent-browser-launcher.js.map +1 -0
  6. package/dist/src/cli.d.ts.map +1 -1
  7. package/dist/src/cli.js +3 -2
  8. package/dist/src/cli.js.map +1 -1
  9. package/dist/src/client.d.ts +36 -7
  10. package/dist/src/client.d.ts.map +1 -1
  11. package/dist/src/client.js +231 -63
  12. package/dist/src/client.js.map +1 -1
  13. package/dist/src/commands/agent-browser.d.ts +3 -0
  14. package/dist/src/commands/agent-browser.d.ts.map +1 -0
  15. package/dist/src/commands/agent-browser.js +27 -0
  16. package/dist/src/commands/agent-browser.js.map +1 -0
  17. package/dist/src/commands/auth.d.ts +1 -1
  18. package/dist/src/commands/auth.d.ts.map +1 -1
  19. package/dist/src/commands/auth.js +4 -2
  20. package/dist/src/commands/auth.js.map +1 -1
  21. package/dist/src/commands/dashboard.d.ts +13 -0
  22. package/dist/src/commands/dashboard.d.ts.map +1 -1
  23. package/dist/src/commands/dashboard.js +10 -18
  24. package/dist/src/commands/dashboard.js.map +1 -1
  25. package/dist/src/commands/helpers.d.ts +1 -1
  26. package/dist/src/commands/helpers.d.ts.map +1 -1
  27. package/dist/src/commands/helpers.js +2 -2
  28. package/dist/src/commands/helpers.js.map +1 -1
  29. package/dist/src/commands/index.d.ts +2 -1
  30. package/dist/src/commands/index.d.ts.map +1 -1
  31. package/dist/src/commands/index.js +2 -1
  32. package/dist/src/commands/index.js.map +1 -1
  33. package/dist/src/commands/mentoring.d.ts.map +1 -1
  34. package/dist/src/commands/mentoring.js +54 -29
  35. package/dist/src/commands/mentoring.js.map +1 -1
  36. package/dist/src/commands/notice.d.ts.map +1 -1
  37. package/dist/src/commands/notice.js +2 -1
  38. package/dist/src/commands/notice.js.map +1 -1
  39. package/dist/src/commands/report.d.ts.map +1 -1
  40. package/dist/src/commands/report.js +4 -2
  41. package/dist/src/commands/report.js.map +1 -1
  42. package/dist/src/commands/room.d.ts.map +1 -1
  43. package/dist/src/commands/room.js +125 -2
  44. package/dist/src/commands/room.js.map +1 -1
  45. package/dist/src/commands/schedule.d.ts +3 -0
  46. package/dist/src/commands/schedule.d.ts.map +1 -0
  47. package/dist/src/commands/schedule.js +27 -0
  48. package/dist/src/commands/schedule.js.map +1 -0
  49. package/dist/src/commands/team.d.ts.map +1 -1
  50. package/dist/src/commands/team.js +55 -4
  51. package/dist/src/commands/team.js.map +1 -1
  52. package/dist/src/constants.d.ts +5 -5
  53. package/dist/src/constants.d.ts.map +1 -1
  54. package/dist/src/constants.js +20 -8
  55. package/dist/src/constants.js.map +1 -1
  56. package/dist/src/credential-manager.d.ts +9 -0
  57. package/dist/src/credential-manager.d.ts.map +1 -1
  58. package/dist/src/credential-manager.js +24 -0
  59. package/dist/src/credential-manager.js.map +1 -1
  60. package/dist/src/formatters.d.ts +11 -3
  61. package/dist/src/formatters.d.ts.map +1 -1
  62. package/dist/src/formatters.js +281 -52
  63. package/dist/src/formatters.js.map +1 -1
  64. package/dist/src/http.d.ts +8 -0
  65. package/dist/src/http.d.ts.map +1 -1
  66. package/dist/src/http.js +29 -1
  67. package/dist/src/http.js.map +1 -1
  68. package/dist/src/index.d.ts +4 -1
  69. package/dist/src/index.d.ts.map +1 -1
  70. package/dist/src/index.js +2 -1
  71. package/dist/src/index.js.map +1 -1
  72. package/dist/src/shared/utils/swmaestro.d.ts +34 -1
  73. package/dist/src/shared/utils/swmaestro.d.ts.map +1 -1
  74. package/dist/src/shared/utils/swmaestro.js +102 -43
  75. package/dist/src/shared/utils/swmaestro.js.map +1 -1
  76. package/dist/src/shared/utils/team-action-params.d.ts +3 -0
  77. package/dist/src/shared/utils/team-action-params.d.ts.map +1 -0
  78. package/dist/src/shared/utils/team-action-params.js +10 -0
  79. package/dist/src/shared/utils/team-action-params.js.map +1 -0
  80. package/dist/src/shared/utils/team-params.d.ts +12 -0
  81. package/dist/src/shared/utils/team-params.d.ts.map +1 -0
  82. package/dist/src/shared/utils/team-params.js +38 -0
  83. package/dist/src/shared/utils/team-params.js.map +1 -0
  84. package/dist/src/types.d.ts +147 -10
  85. package/dist/src/types.d.ts.map +1 -1
  86. package/dist/src/types.js +74 -6
  87. package/dist/src/types.js.map +1 -1
  88. package/package.json +5 -1
  89. package/src/agent-browser-launcher.test.ts +263 -0
  90. package/src/agent-browser-launcher.ts +159 -0
  91. package/src/cli.ts +4 -2
  92. package/src/client.test.ts +801 -140
  93. package/src/client.ts +293 -79
  94. package/src/commands/agent-browser.ts +33 -0
  95. package/src/commands/auth.test.ts +83 -32
  96. package/src/commands/auth.ts +5 -3
  97. package/src/commands/dashboard.test.ts +57 -0
  98. package/src/commands/dashboard.ts +22 -19
  99. package/src/commands/helpers.test.ts +79 -32
  100. package/src/commands/helpers.ts +3 -3
  101. package/src/commands/index.ts +2 -1
  102. package/src/commands/mentoring.ts +60 -29
  103. package/src/commands/notice.ts +2 -1
  104. package/src/commands/report.test.ts +7 -7
  105. package/src/commands/report.ts +4 -2
  106. package/src/commands/room.ts +160 -1
  107. package/src/commands/schedule.ts +32 -0
  108. package/src/commands/team.ts +73 -5
  109. package/src/constants.ts +20 -8
  110. package/src/credential-manager.test.ts +49 -5
  111. package/src/credential-manager.ts +27 -0
  112. package/src/formatters.test.ts +548 -53
  113. package/src/formatters.ts +309 -55
  114. package/src/http.test.ts +108 -39
  115. package/src/http.ts +41 -2
  116. package/src/index.ts +10 -1
  117. package/src/shared/utils/mentoring-params.test.ts +16 -16
  118. package/src/shared/utils/swmaestro.test.ts +326 -11
  119. package/src/shared/utils/swmaestro.ts +150 -52
  120. package/src/shared/utils/team-action-params.test.ts +32 -0
  121. package/src/shared/utils/team-action-params.ts +10 -0
  122. package/src/shared/utils/team-params.test.ts +141 -0
  123. package/src/shared/utils/team-params.ts +53 -0
  124. package/src/shared/utils/toz.test.ts +12 -7
  125. package/src/token-extractor.test.ts +12 -12
  126. package/src/toz-http.test.ts +11 -11
  127. package/src/types.test.ts +235 -206
  128. package/src/types.ts +87 -7
  129. package/dist/src/commands/event.d.ts +0 -3
  130. package/dist/src/commands/event.d.ts.map +0 -1
  131. package/dist/src/commands/event.js +0 -58
  132. package/dist/src/commands/event.js.map +0 -1
  133. package/src/commands/event.ts +0 -73
@@ -0,0 +1,263 @@
1
+ import { afterEach, describe, expect, it } from 'bun:test'
2
+ import { existsSync } from 'node:fs'
3
+ import { mkdtemp, readFile, readdir, rm, stat } from 'node:fs/promises'
4
+ import { tmpdir } from 'node:os'
5
+ import { join } from 'node:path'
6
+
7
+ import { AgentBrowserLauncher, assertSwmaestroUrl, buildStorageState } from './agent-browser-launcher'
8
+ import type { Spawner } from './agent-browser-launcher'
9
+
10
+ let createdDirs: string[] = []
11
+
12
+ afterEach(async () => {
13
+ for (const dir of createdDirs) {
14
+ await rm(dir, { recursive: true, force: true })
15
+ }
16
+ createdDirs = []
17
+ })
18
+
19
+ describe('buildStorageState', () => {
20
+ it('returns a Playwright-compatible storage state with JSESSIONID host-only on www.swmaestro.ai', () => {
21
+ const state = buildStorageState('test-session-value')
22
+
23
+ expect(state).toEqual({
24
+ cookies: [
25
+ {
26
+ name: 'JSESSIONID',
27
+ value: 'test-session-value',
28
+ domain: 'www.swmaestro.ai',
29
+ path: '/',
30
+ expires: -1,
31
+ httpOnly: true,
32
+ secure: true,
33
+ sameSite: 'Lax',
34
+ },
35
+ ],
36
+ origins: [],
37
+ })
38
+ })
39
+
40
+ it('does not use a leading-dot domain (host-only, not domain cookie)', () => {
41
+ const state = buildStorageState('x')
42
+ expect(state.cookies[0]?.domain).toBe('www.swmaestro.ai')
43
+ expect(state.cookies[0]?.domain.startsWith('.')).toBe(false)
44
+ })
45
+ })
46
+
47
+ describe('assertSwmaestroUrl', () => {
48
+ it('accepts https://www.swmaestro.ai URLs', () => {
49
+ expect(() => assertSwmaestroUrl('https://www.swmaestro.ai/sw/main/main.do')).not.toThrow()
50
+ })
51
+
52
+ it('accepts the bare swmaestro.ai apex', () => {
53
+ expect(() => assertSwmaestroUrl('https://swmaestro.ai/')).not.toThrow()
54
+ })
55
+
56
+ it('rejects non-swmaestro hosts', () => {
57
+ expect(() => assertSwmaestroUrl('https://evil.example.com/path')).toThrow(/swmaestro\.ai/)
58
+ })
59
+
60
+ it('rejects http:// (forces https)', () => {
61
+ expect(() => assertSwmaestroUrl('http://www.swmaestro.ai/')).toThrow(/https/)
62
+ })
63
+
64
+ it('rejects malformed URLs', () => {
65
+ expect(() => assertSwmaestroUrl('not-a-url')).toThrow(/Invalid URL/)
66
+ })
67
+ })
68
+
69
+ describe('AgentBrowserLauncher.launch', () => {
70
+ it('writes a state file with the JSESSIONID and invokes agent-browser with --state and open', async () => {
71
+ const tmpRoot = await makeTempRoot()
72
+ const allCommands: string[][] = []
73
+ let stateContent = ''
74
+ let stateMode = 0
75
+ let dirMode = 0
76
+
77
+ const spawn: Spawner = (command) => ({
78
+ exited: (async () => {
79
+ allCommands.push([...command])
80
+ if (command[1] === '--state') {
81
+ const statePath = command[2]
82
+ if (!statePath) throw new Error('missing state path argument')
83
+ stateContent = await readFile(statePath, 'utf8')
84
+ stateMode = (await stat(statePath)).mode & 0o777
85
+ const dir = statePath.replace(/\/state\.json$/, '')
86
+ dirMode = (await stat(dir)).mode & 0o777
87
+ }
88
+ return 0
89
+ })(),
90
+ })
91
+
92
+ const launcher = new AgentBrowserLauncher({ spawn, binary: 'agent-browser', tmpDir: tmpRoot })
93
+ const result = await launcher.launch({
94
+ url: 'https://www.swmaestro.ai/sw/mypage/myMain/dashboard.do',
95
+ sessionCookie: 'abc-123-jsessionid',
96
+ })
97
+
98
+ expect(result.exitCode).toBe(0)
99
+ expect(allCommands[0]).toEqual(['agent-browser', 'close', '--all'])
100
+
101
+ const launchCmd = allCommands[1]
102
+ expect(launchCmd?.[0]).toBe('agent-browser')
103
+ expect(launchCmd?.[1]).toBe('--state')
104
+ expect(launchCmd?.[3]).toBe('open')
105
+ expect(launchCmd?.[4]).toBe('https://www.swmaestro.ai/sw/mypage/myMain/dashboard.do')
106
+
107
+ const parsed = JSON.parse(stateContent) as { cookies: Array<{ name: string; value: string }> }
108
+ expect(parsed.cookies[0]?.name).toBe('JSESSIONID')
109
+ expect(parsed.cookies[0]?.value).toBe('abc-123-jsessionid')
110
+
111
+ expect(stateMode).toBe(0o600)
112
+ expect(dirMode).toBe(0o700)
113
+ })
114
+
115
+ it('closes any running daemon before launching so --state is not silently ignored', async () => {
116
+ const tmpRoot = await makeTempRoot()
117
+ const calls: string[] = []
118
+
119
+ const spawn: Spawner = (command) => ({
120
+ exited: (async () => {
121
+ if (command[1] === 'close') calls.push('close')
122
+ if (command[1] === '--state') calls.push('launch')
123
+ return 0
124
+ })(),
125
+ })
126
+
127
+ await new AgentBrowserLauncher({ spawn, tmpDir: tmpRoot }).launch({
128
+ url: 'https://www.swmaestro.ai/sw/main/main.do',
129
+ sessionCookie: 'x',
130
+ })
131
+
132
+ expect(calls).toEqual(['close', 'launch'])
133
+ })
134
+
135
+ it('never includes the JSESSIONID value in argv', async () => {
136
+ const tmpRoot = await makeTempRoot()
137
+ const allArgs: string[] = []
138
+
139
+ const spawn: Spawner = (command) => ({
140
+ exited: (async () => {
141
+ allArgs.push(...command)
142
+ return 0
143
+ })(),
144
+ })
145
+
146
+ const launcher = new AgentBrowserLauncher({ spawn, tmpDir: tmpRoot })
147
+ await launcher.launch({
148
+ url: 'https://www.swmaestro.ai/sw/main/main.do',
149
+ sessionCookie: 'super-secret-jsessionid-value',
150
+ })
151
+
152
+ for (const arg of allArgs) {
153
+ expect(arg).not.toContain('super-secret-jsessionid-value')
154
+ }
155
+ })
156
+
157
+ it('cleans up the state directory after agent-browser exits successfully', async () => {
158
+ const tmpRoot = await makeTempRoot()
159
+ let capturedStatePath = ''
160
+
161
+ const spawn: Spawner = (command) => ({
162
+ exited: (async () => {
163
+ if (command[1] === '--state') {
164
+ capturedStatePath = command[2] ?? ''
165
+ }
166
+ return 0
167
+ })(),
168
+ })
169
+
170
+ await new AgentBrowserLauncher({ spawn, tmpDir: tmpRoot }).launch({
171
+ url: 'https://www.swmaestro.ai/sw/main/main.do',
172
+ sessionCookie: 'x',
173
+ })
174
+
175
+ expect(existsSync(capturedStatePath)).toBe(false)
176
+ const remaining = await readdir(tmpRoot)
177
+ expect(remaining).toEqual([])
178
+ })
179
+
180
+ it('cleans up the state directory even if agent-browser throws', async () => {
181
+ const tmpRoot = await makeTempRoot()
182
+ let capturedStatePath = ''
183
+
184
+ const spawn: Spawner = (command) => ({
185
+ exited: (async () => {
186
+ if (command[1] === '--state') {
187
+ capturedStatePath = command[2] ?? ''
188
+ throw new Error('boom')
189
+ }
190
+ return 0
191
+ })(),
192
+ })
193
+
194
+ await expect(
195
+ new AgentBrowserLauncher({ spawn, tmpDir: tmpRoot }).launch({
196
+ url: 'https://www.swmaestro.ai/sw/main/main.do',
197
+ sessionCookie: 'x',
198
+ }),
199
+ ).rejects.toThrow('boom')
200
+
201
+ expect(existsSync(capturedStatePath)).toBe(false)
202
+ const remaining = await readdir(tmpRoot)
203
+ expect(remaining).toEqual([])
204
+ })
205
+
206
+ it('propagates the agent-browser non-zero exit code', async () => {
207
+ const tmpRoot = await makeTempRoot()
208
+ const spawn: Spawner = (command) => ({
209
+ exited: Promise.resolve(command[1] === '--state' ? 42 : 0),
210
+ })
211
+
212
+ const result = await new AgentBrowserLauncher({ spawn, tmpDir: tmpRoot }).launch({
213
+ url: 'https://www.swmaestro.ai/sw/main/main.do',
214
+ sessionCookie: 'x',
215
+ })
216
+
217
+ expect(result.exitCode).toBe(42)
218
+ })
219
+
220
+ it('uses a custom binary path when provided', async () => {
221
+ const tmpRoot = await makeTempRoot()
222
+ const observedBinaries: string[] = []
223
+ const spawn: Spawner = (command) => ({
224
+ exited: (async () => {
225
+ observedBinaries.push(command[0] ?? '')
226
+ return 0
227
+ })(),
228
+ })
229
+
230
+ await new AgentBrowserLauncher({ spawn, tmpDir: tmpRoot, binary: '/custom/path/agent-browser' }).launch({
231
+ url: 'https://www.swmaestro.ai/sw/main/main.do',
232
+ sessionCookie: 'x',
233
+ })
234
+
235
+ expect(new Set(observedBinaries)).toEqual(new Set(['/custom/path/agent-browser']))
236
+ })
237
+
238
+ it('rejects non-swmaestro URLs before spawning anything', async () => {
239
+ const tmpRoot = await makeTempRoot()
240
+ let spawnCalled = false
241
+ const spawn: Spawner = () => {
242
+ spawnCalled = true
243
+ return { exited: Promise.resolve(0) }
244
+ }
245
+
246
+ await expect(
247
+ new AgentBrowserLauncher({ spawn, tmpDir: tmpRoot }).launch({
248
+ url: 'https://evil.example.com/path',
249
+ sessionCookie: 'x',
250
+ }),
251
+ ).rejects.toThrow(/swmaestro\.ai/)
252
+
253
+ expect(spawnCalled).toBe(false)
254
+ const remaining = await readdir(tmpRoot)
255
+ expect(remaining).toEqual([])
256
+ })
257
+ })
258
+
259
+ async function makeTempRoot(): Promise<string> {
260
+ const dir = await mkdtemp(join(tmpdir(), 'opensoma-launcher-test-'))
261
+ createdDirs.push(dir)
262
+ return dir
263
+ }
@@ -0,0 +1,159 @@
1
+ import { randomBytes } from 'node:crypto'
2
+ import { chmod, mkdtemp, rm, writeFile } from 'node:fs/promises'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
5
+
6
+ // Minimal declaration so the web workspace tsconfig (which lacks @types/bun)
7
+ // can type-check this file without error. The real implementation always runs
8
+ // under Bun, which provides the full global at runtime.
9
+ declare const Bun: {
10
+ spawn(
11
+ command: string[],
12
+ options: { stdout: 'inherit'; stderr: 'inherit'; stdin: 'inherit' },
13
+ ): { exited: Promise<number> }
14
+ }
15
+
16
+ export interface AgentBrowserLaunchInput {
17
+ url: string
18
+ sessionCookie: string
19
+ }
20
+
21
+ export interface SpawnedProcess {
22
+ exited: Promise<number>
23
+ }
24
+
25
+ export type Spawner = (command: string[]) => SpawnedProcess
26
+
27
+ export interface AgentBrowserLauncherOptions {
28
+ spawn?: Spawner
29
+ binary?: string
30
+ tmpDir?: string
31
+ }
32
+
33
+ export interface LaunchResult {
34
+ exitCode: number
35
+ statePath: string
36
+ }
37
+
38
+ const SWMAESTRO_HOST = 'www.swmaestro.ai'
39
+ const ALLOWED_HOSTS = new Set([SWMAESTRO_HOST, 'swmaestro.ai'])
40
+
41
+ interface StorageStateCookie {
42
+ name: string
43
+ value: string
44
+ domain: string
45
+ path: string
46
+ expires: number
47
+ httpOnly: boolean
48
+ secure: boolean
49
+ sameSite: 'Strict' | 'Lax' | 'None'
50
+ }
51
+
52
+ interface StorageState {
53
+ cookies: StorageStateCookie[]
54
+ origins: never[]
55
+ }
56
+
57
+ export function buildStorageState(sessionCookie: string): StorageState {
58
+ // Java EE JSESSIONID is host-only on the exact host that issued it. Use
59
+ // www.swmaestro.ai (no leading dot) to match how the native server sets it.
60
+ // Using `.swmaestro.ai` here would either fail to import or produce a
61
+ // domain-cookie that the server treats as foreign.
62
+ return {
63
+ cookies: [
64
+ {
65
+ name: 'JSESSIONID',
66
+ value: sessionCookie,
67
+ domain: SWMAESTRO_HOST,
68
+ path: '/',
69
+ // -1 is the Playwright/agent-browser convention for session cookies
70
+ // (no Expires/Max-Age attribute). JSESSIONID is a session cookie.
71
+ expires: -1,
72
+ httpOnly: true,
73
+ secure: true,
74
+ sameSite: 'Lax',
75
+ },
76
+ ],
77
+ origins: [],
78
+ }
79
+ }
80
+
81
+ export function assertSwmaestroUrl(rawUrl: string): URL {
82
+ let parsed: URL
83
+ try {
84
+ parsed = new URL(rawUrl)
85
+ } catch {
86
+ throw new Error(`Invalid URL: ${rawUrl}`)
87
+ }
88
+
89
+ if (parsed.protocol !== 'https:') {
90
+ throw new Error(`URL must use https:// (got ${parsed.protocol}//): ${rawUrl}`)
91
+ }
92
+
93
+ if (!ALLOWED_HOSTS.has(parsed.hostname)) {
94
+ throw new Error(
95
+ `URL must point to swmaestro.ai (got ${parsed.hostname}). Cookie injection only works for swmaestro.ai targets.`,
96
+ )
97
+ }
98
+
99
+ return parsed
100
+ }
101
+
102
+ function defaultSpawn(command: string[]): SpawnedProcess {
103
+ // Bun.spawn with array args avoids shell interpretation entirely. The
104
+ // randomized state-file path appears in argv but the JSESSIONID value
105
+ // never does — that's the security guarantee.
106
+ const proc = Bun.spawn(command, {
107
+ stdout: 'inherit',
108
+ stderr: 'inherit',
109
+ stdin: 'inherit',
110
+ })
111
+ return { exited: proc.exited }
112
+ }
113
+
114
+ export class AgentBrowserLauncher {
115
+ private readonly spawn: Spawner
116
+ private readonly binary: string
117
+ private readonly tmpRoot: string
118
+
119
+ constructor(options: AgentBrowserLauncherOptions = {}) {
120
+ this.spawn = options.spawn ?? defaultSpawn
121
+ this.binary = options.binary ?? 'agent-browser'
122
+ this.tmpRoot = options.tmpDir ?? tmpdir()
123
+ }
124
+
125
+ async launch(input: AgentBrowserLaunchInput): Promise<LaunchResult> {
126
+ const target = assertSwmaestroUrl(input.url)
127
+ const state = buildStorageState(input.sessionCookie)
128
+
129
+ // agent-browser ignores --state if a daemon is already running. Close
130
+ // first so our cookie injection actually takes effect. We don't care
131
+ // about the close exit code (no-op if no daemon was running).
132
+ await this.spawn([this.binary, 'close', '--all']).exited
133
+
134
+ const stateDir = await this.createStateDir()
135
+ const statePath = join(stateDir, 'state.json')
136
+
137
+ try {
138
+ await writeFile(statePath, JSON.stringify(state), { mode: 0o600 })
139
+ await chmod(statePath, 0o600)
140
+
141
+ const proc = this.spawn([this.binary, '--state', statePath, 'open', target.toString()])
142
+ const exitCode = await proc.exited
143
+ return { exitCode, statePath }
144
+ } finally {
145
+ await rm(stateDir, { recursive: true, force: true })
146
+ }
147
+ }
148
+
149
+ private async createStateDir(): Promise<string> {
150
+ // Random suffix on the directory name so concurrent launches don't
151
+ // collide and so the path is hard to predict for same-UID local
152
+ // attackers. mkdtemp creates with 0700 by default on macOS/Linux but we
153
+ // chmod to be explicit across platforms.
154
+ const prefix = `opensoma-agent-browser-${randomBytes(8).toString('hex')}-`
155
+ const dir = await mkdtemp(join(this.tmpRoot, prefix))
156
+ await chmod(dir, 0o700)
157
+ return dir
158
+ }
159
+ }
package/src/cli.ts CHANGED
@@ -4,14 +4,15 @@ import { Command } from 'commander'
4
4
 
5
5
  import pkg from '../package.json' with { type: 'json' }
6
6
  import {
7
+ agentBrowserCommand,
7
8
  authCommand,
8
9
  dashboardCommand,
9
- eventCommand,
10
10
  memberCommand,
11
11
  mentoringCommand,
12
12
  noticeCommand,
13
13
  reportCommand,
14
14
  roomCommand,
15
+ scheduleCommand,
15
16
  teamCommand,
16
17
  } from './commands/index'
17
18
 
@@ -45,13 +46,14 @@ program.hook('preAction', async (_thisCommand, actionCommand) => {
45
46
  })
46
47
 
47
48
  program.addCommand(authCommand)
49
+ program.addCommand(agentBrowserCommand)
48
50
  program.addCommand(mentoringCommand)
49
51
  program.addCommand(roomCommand)
50
52
  program.addCommand(dashboardCommand)
51
53
  program.addCommand(noticeCommand)
52
54
  program.addCommand(teamCommand)
53
55
  program.addCommand(memberCommand)
54
- program.addCommand(eventCommand)
56
+ program.addCommand(scheduleCommand)
55
57
  program.addCommand(reportCommand)
56
58
 
57
59
  program.parse(process.argv)