izteamslots 1.1.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.
- package/.env.example +1 -0
- package/CONTRIBUTING.md +128 -0
- package/README.md +249 -0
- package/app.py +25 -0
- package/backend/__init__.py +3 -0
- package/backend/__main__.py +3 -0
- package/backend/account_store.py +448 -0
- package/backend/chatgpt_workspace_api.py +104 -0
- package/backend/dto.py +106 -0
- package/backend/file_logger.py +82 -0
- package/backend/jobs.py +77 -0
- package/backend/mail/__init__.py +98 -0
- package/backend/mail/base.py +86 -0
- package/backend/mail/boomlify.py +178 -0
- package/backend/mail/imap.py +221 -0
- package/backend/mail/trickads.py +121 -0
- package/backend/openai_web_auth.py +1402 -0
- package/backend/rpc_protocol.py +78 -0
- package/backend/rpc_server.py +233 -0
- package/backend/slot_orchestrator.py +400 -0
- package/backend/ui_facade.py +368 -0
- package/bin/izteamslots.sh +16 -0
- package/package.json +30 -0
- package/requirements.txt +2 -0
- package/scripts/setup.sh +82 -0
- package/ui/package.json +19 -0
- package/ui/src/main.ts +4 -0
- package/ui/src/menus/format.ts +163 -0
- package/ui/src/menus/mainMenus.ts +221 -0
- package/ui/src/menus/types.ts +75 -0
- package/ui/src/screens/MainScreen.ts +1175 -0
- package/ui/src/transport/stdioClient.ts +162 -0
- package/ui/tsconfig.json +13 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { spawn, type ChildProcessByStdio } from "node:child_process"
|
|
2
|
+
import type { Readable, Writable } from "node:stream"
|
|
3
|
+
import { randomUUID } from "node:crypto"
|
|
4
|
+
import { dirname, resolve } from "node:path"
|
|
5
|
+
import { createInterface } from "node:readline"
|
|
6
|
+
import { fileURLToPath } from "node:url"
|
|
7
|
+
|
|
8
|
+
type RpcEvent = {
|
|
9
|
+
type: "event"
|
|
10
|
+
event: string
|
|
11
|
+
data: Record<string, unknown>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type RpcResponse = {
|
|
15
|
+
type: "response"
|
|
16
|
+
id: string
|
|
17
|
+
ok: boolean
|
|
18
|
+
result?: unknown
|
|
19
|
+
error?: { code: number; message: string; data?: unknown }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type EventHandler = (event: RpcEvent) => void
|
|
23
|
+
export type ErrorOutputHandler = (line: string) => void
|
|
24
|
+
|
|
25
|
+
function projectRootFromCurrentFile() {
|
|
26
|
+
const here = dirname(fileURLToPath(import.meta.url))
|
|
27
|
+
return resolve(here, "../../..")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class StdioRpcClient {
|
|
31
|
+
private proc: ChildProcessByStdio<Writable, Readable, Readable> | null = null
|
|
32
|
+
private readonly pending = new Map<
|
|
33
|
+
string,
|
|
34
|
+
{
|
|
35
|
+
resolve: (value: unknown) => void
|
|
36
|
+
reject: (reason?: unknown) => void
|
|
37
|
+
}
|
|
38
|
+
>()
|
|
39
|
+
private readonly eventHandlers = new Set<EventHandler>()
|
|
40
|
+
private readonly errorHandlers = new Set<ErrorOutputHandler>()
|
|
41
|
+
|
|
42
|
+
constructor(
|
|
43
|
+
private readonly pythonCmd: string =
|
|
44
|
+
process.env.PYTHON_BIN ?? process.env.PYTHON ?? "python3",
|
|
45
|
+
private readonly projectRoot: string = process.env.IZTEAMSLOTS_ROOT ?? projectRootFromCurrentFile(),
|
|
46
|
+
) {}
|
|
47
|
+
|
|
48
|
+
start() {
|
|
49
|
+
if (this.proc) return
|
|
50
|
+
|
|
51
|
+
this.proc = spawn(this.pythonCmd, ["-m", "backend"], {
|
|
52
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
53
|
+
cwd: this.projectRoot,
|
|
54
|
+
env: {
|
|
55
|
+
...process.env,
|
|
56
|
+
PYTHONPATH: process.env.PYTHONPATH
|
|
57
|
+
? `${this.projectRoot}:${process.env.PYTHONPATH}`
|
|
58
|
+
: this.projectRoot,
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const proc = this.proc
|
|
63
|
+
if (!proc) {
|
|
64
|
+
throw new Error("RPC process did not start")
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
proc.on("error", (err) => {
|
|
68
|
+
this.proc = null
|
|
69
|
+
for (const [, p] of this.pending) {
|
|
70
|
+
p.reject(new Error(`RPC spawn error: ${err.message}`))
|
|
71
|
+
}
|
|
72
|
+
this.pending.clear()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const rl = createInterface({ input: proc.stdout })
|
|
76
|
+
const stderr = createInterface({ input: proc.stderr })
|
|
77
|
+
|
|
78
|
+
stderr.on("line", (line) => {
|
|
79
|
+
const message = line.trim()
|
|
80
|
+
if (!message) return
|
|
81
|
+
for (const handler of this.errorHandlers) handler(message)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
rl.on("line", (line) => {
|
|
85
|
+
if (!line.trim()) return
|
|
86
|
+
let payload: RpcEvent | RpcResponse
|
|
87
|
+
try {
|
|
88
|
+
payload = JSON.parse(line) as RpcEvent | RpcResponse
|
|
89
|
+
} catch {
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (payload.type === "event") {
|
|
94
|
+
for (const handler of this.eventHandlers) handler(payload)
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const entry = this.pending.get(payload.id)
|
|
99
|
+
if (!entry) return
|
|
100
|
+
this.pending.delete(payload.id)
|
|
101
|
+
if (payload.ok) {
|
|
102
|
+
entry.resolve(payload.result)
|
|
103
|
+
} else {
|
|
104
|
+
entry.reject(new Error(payload.error?.message ?? "RPC error"))
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
proc.on("exit", (code, signal) => {
|
|
109
|
+
this.proc = null
|
|
110
|
+
const suffix = signal ? `signal ${signal}` : `code ${String(code ?? "unknown")}`
|
|
111
|
+
for (const handler of this.errorHandlers) handler(`RPC backend остановлен (${suffix})`)
|
|
112
|
+
for (const [id, p] of this.pending) {
|
|
113
|
+
p.reject(new Error(`RPC process exited before response: ${id}`))
|
|
114
|
+
}
|
|
115
|
+
this.pending.clear()
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
onEvent(handler: EventHandler): () => void {
|
|
120
|
+
this.eventHandlers.add(handler)
|
|
121
|
+
return () => this.eventHandlers.delete(handler)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
onErrorOutput(handler: ErrorOutputHandler): () => void {
|
|
125
|
+
this.errorHandlers.add(handler)
|
|
126
|
+
return () => this.errorHandlers.delete(handler)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async request<T>(method: string, params: Record<string, unknown> = {}): Promise<T> {
|
|
130
|
+
this.start()
|
|
131
|
+
const id = randomUUID()
|
|
132
|
+
const message = JSON.stringify({ id, method, params })
|
|
133
|
+
|
|
134
|
+
if (!this.proc) {
|
|
135
|
+
throw new Error("RPC process is not running")
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return await new Promise<T>((resolve, reject) => {
|
|
139
|
+
this.pending.set(id, {
|
|
140
|
+
resolve: (value) => resolve(value as T),
|
|
141
|
+
reject,
|
|
142
|
+
})
|
|
143
|
+
if (!this.proc) {
|
|
144
|
+
reject(new Error("RPC process is not running"))
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
this.proc.stdin.write(message + "\n")
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async shutdown() {
|
|
152
|
+
try {
|
|
153
|
+
await this.request("shutdown")
|
|
154
|
+
} catch {
|
|
155
|
+
// no-op
|
|
156
|
+
}
|
|
157
|
+
if (this.proc) {
|
|
158
|
+
this.proc.kill()
|
|
159
|
+
this.proc = null
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
package/ui/tsconfig.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"resolveJsonModule": true,
|
|
10
|
+
"types": ["node", "bun"]
|
|
11
|
+
},
|
|
12
|
+
"include": ["src/**/*.ts", "src/**/*.tsx"]
|
|
13
|
+
}
|