@jpetit/toolkit 3.0.5 → 3.0.6
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 +11 -5
- package/.prettierignore +0 -11
- package/.prettierrc.json +0 -9
- package/.vscode/settings.json +0 -26
- package/assets.zip +0 -0
- package/eslint.config.mjs +0 -31
- package/lib/ai.ts +0 -138
- package/lib/assets.ts +0 -31
- package/lib/cleaner.ts +0 -58
- package/lib/compilers/_frompython.ts +0 -388
- package/lib/compilers/base.ts +0 -97
- package/lib/compilers/gcc.ts +0 -47
- package/lib/compilers/gxx.ts +0 -47
- package/lib/compilers/index.ts +0 -61
- package/lib/compilers/python3.ts +0 -67
- package/lib/data.ts +0 -19
- package/lib/doctor.ts +0 -105
- package/lib/generate.ts +0 -333
- package/lib/maker.ts +0 -523
- package/lib/settings.ts +0 -42
- package/lib/tui.ts +0 -70
- package/lib/utils.ts +0 -83
- package/problems/maxim2.pbm/Main.java +0 -13
- package/problems/maxim2.pbm/distillation.yml +0 -7
- package/problems/maxim2.pbm/distilled-01.inp +0 -1
- package/problems/maxim2.pbm/distilled-02.inp +0 -1
- package/problems/maxim2.pbm/distilled-03.inp +0 -1
- package/problems/maxim2.pbm/distiller.yml +0 -2
- package/problems/maxim2.pbm/generate-inputs.py +0 -9
- package/problems/maxim2.pbm/handler.yml +0 -2
- package/problems/maxim2.pbm/ma-1.inp +0 -1
- package/problems/maxim2.pbm/ma-2.inp +0 -1
- package/problems/maxim2.pbm/ma-3.inp +0 -1
- package/problems/maxim2.pbm/ma-4.inp +0 -1
- package/problems/maxim2.pbm/ma-5.inp +0 -1
- package/problems/maxim2.pbm/per-doubles.inp +0 -1
- package/problems/maxim2.pbm/problem.ca.html +0 -11
- package/problems/maxim2.pbm/problem.ca.md +0 -19
- package/problems/maxim2.pbm/problem.ca.tex +0 -17
- package/problems/maxim2.pbm/problem.ca.txt +0 -19
- package/problems/maxim2.pbm/problem.ca.yml +0 -3
- package/problems/maxim2.pbm/problem.en.html +0 -11
- package/problems/maxim2.pbm/problem.en.md +0 -19
- package/problems/maxim2.pbm/problem.en.tex +0 -16
- package/problems/maxim2.pbm/problem.en.txt +0 -19
- package/problems/maxim2.pbm/problem.en.yml +0 -4
- package/problems/maxim2.pbm/sample-1.inp +0 -1
- package/problems/maxim2.pbm/sample-2.inp +0 -1
- package/problems/maxim2.pbm/sample-3.inp +0 -1
- package/problems/maxim2.pbm/solution.c +0 -12
- package/problems/maxim2.pbm/solution.cc +0 -13
- package/problems/maxim2.pbm/solution.java +0 -13
- package/problems/maxim2.pbm/solution.pas +0 -9
- package/problems/maxim2.pbm/solution.py +0 -5
- package/problems/maxim2.pbm/tags.yml +0 -2
- package/problems/maxim2.pbm/test_-1_-1.inp +0 -1
- package/problems/maxim2.pbm/test_-1_-2.inp +0 -1
- package/problems/maxim2.pbm/test_-1_0.inp +0 -1
- package/problems/maxim2.pbm/test_-1_1.inp +0 -1
- package/problems/maxim2.pbm/test_-2_-1.inp +0 -1
- package/problems/maxim2.pbm/test_-2_-2.inp +0 -1
- package/problems/maxim2.pbm/test_-2_0.inp +0 -1
- package/problems/maxim2.pbm/test_-2_1.inp +0 -1
- package/problems/maxim2.pbm/test_0_-1.inp +0 -1
- package/problems/maxim2.pbm/test_0_-2.inp +0 -1
- package/problems/maxim2.pbm/test_0_0.inp +0 -1
- package/problems/maxim2.pbm/test_0_1.inp +0 -1
- package/problems/maxim2.pbm/test_1_-1.inp +0 -1
- package/problems/maxim2.pbm/test_1_-2.inp +0 -1
- package/problems/maxim2.pbm/test_1_0.inp +0 -1
- package/problems/maxim2.pbm/test_1_1.inp +0 -1
- package/test.ts +0 -3
- package/toolkit/ai.ts +0 -30
- package/toolkit/clean.ts +0 -19
- package/toolkit/compilers.ts +0 -29
- package/toolkit/create-jutge-ai.ts +0 -101
- package/toolkit/create-template.ts +0 -51
- package/toolkit/create-wizard.ts +0 -4
- package/toolkit/create.ts +0 -65
- package/toolkit/doctor.ts +0 -17
- package/toolkit/init.ts +0 -59
- package/toolkit/make.ts +0 -60
- package/toolkit/verify.ts +0 -19
- package/tsconfig.json +0 -38
- package/types/zip.d.ts +0 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jpetit/toolkit",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.6",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -8,13 +8,16 @@
|
|
|
8
8
|
"type": "module",
|
|
9
9
|
"bin": "toolkit/index.ts",
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "bun run build-assets && bun build toolkit --compile --outfile toolkit.exe",
|
|
12
|
-
"build-assets": "rm assets.zip && zip assets.zip -r assets",
|
|
13
11
|
"lint": "eslint .",
|
|
14
12
|
"format": "bun prettier . --write",
|
|
15
13
|
"depcheck": "bunx depcheck --ignores='bun,bun:test'"
|
|
16
14
|
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"assets"
|
|
18
|
+
],
|
|
17
19
|
"dependencies": {
|
|
20
|
+
"@commander-js/extra-typings": "^14.0.0",
|
|
18
21
|
"@eslint/js": "^9.39.2",
|
|
19
22
|
"@inquirer/prompts": "^8.1.0",
|
|
20
23
|
"boxen": "^8.0.1",
|
|
@@ -23,7 +26,9 @@
|
|
|
23
26
|
"commander": "^14.0.2",
|
|
24
27
|
"env-paths": "^3.0.0",
|
|
25
28
|
"eslint": "^9.39.2",
|
|
29
|
+
"execa": "^9.6.1",
|
|
26
30
|
"fflate": "^0.8.2",
|
|
31
|
+
"glob": "^13.0.0",
|
|
27
32
|
"gpt-tokenizer": "^3.4.0",
|
|
28
33
|
"human-id": "^4.1.3",
|
|
29
34
|
"image-size": "^2.0.2",
|
|
@@ -32,21 +37,22 @@
|
|
|
32
37
|
"multi-llm-ts": "^4.6.2",
|
|
33
38
|
"nanoid": "^5.1.6",
|
|
34
39
|
"ora": "^9.0.0",
|
|
35
|
-
"@commander-js/extra-typings": "^14.0.0",
|
|
36
40
|
"prettier": "^3.7.4",
|
|
37
41
|
"pretty-bytes": "^7.1.0",
|
|
38
42
|
"pretty-ms": "^9.3.0",
|
|
39
43
|
"radash": "^12.1.1",
|
|
40
44
|
"slug": "^11.0.1",
|
|
41
45
|
"typescript-eslint": "^8.50.0",
|
|
46
|
+
"yaml": "^2.8.2",
|
|
42
47
|
"zod": "^4.2.1"
|
|
43
48
|
},
|
|
44
49
|
"devDependencies": {
|
|
50
|
+
"@types/bun": "1.3.5",
|
|
45
51
|
"@types/image-size": "^0.8.0",
|
|
46
52
|
"@types/marked": "^6.0.0",
|
|
47
53
|
"@types/marked-terminal": "^6.1.1",
|
|
54
|
+
"@types/node": "^25.0.3",
|
|
48
55
|
"@types/ora": "^3.2.0",
|
|
49
|
-
"@types/bun": "1.3.5",
|
|
50
56
|
"@types/slug": "^5.0.9"
|
|
51
57
|
},
|
|
52
58
|
"peerDependencies": {
|
package/.prettierignore
DELETED
package/.prettierrc.json
DELETED
package/.vscode/settings.json
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
3
|
-
"[javascript]": {
|
|
4
|
-
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
5
|
-
},
|
|
6
|
-
"editor.tabSize": 4,
|
|
7
|
-
"editor.insertSpaces": true,
|
|
8
|
-
"editor.indentSize": "tabSize",
|
|
9
|
-
"editor.detectIndentation": false,
|
|
10
|
-
"files.exclude": {
|
|
11
|
-
"**/node_modules": true,
|
|
12
|
-
"**/.next": true,
|
|
13
|
-
"**/data/quizzes": true,
|
|
14
|
-
"**/prisma/generated": true
|
|
15
|
-
},
|
|
16
|
-
"explorer.sortOrder": "filesFirst",
|
|
17
|
-
"[c]": {
|
|
18
|
-
"editor.defaultFormatter": "ms-vscode.cpptools"
|
|
19
|
-
},
|
|
20
|
-
"[java]": {
|
|
21
|
-
"editor.defaultFormatter": "redhat.java"
|
|
22
|
-
},
|
|
23
|
-
"[cpp]": {
|
|
24
|
-
"editor.defaultFormatter": "ms-vscode.cpptools"
|
|
25
|
-
}
|
|
26
|
-
}
|
package/assets.zip
DELETED
|
Binary file
|
package/eslint.config.mjs
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
|
-
import eslint from '@eslint/js'
|
|
4
|
-
import { globalIgnores } from 'eslint/config'
|
|
5
|
-
import tseslint from 'typescript-eslint'
|
|
6
|
-
|
|
7
|
-
export default tseslint.config(
|
|
8
|
-
eslint.configs.recommended,
|
|
9
|
-
tseslint.configs.recommended,
|
|
10
|
-
[globalIgnores(['postcss.config.mjs', 'eslint.config.mjs', 'lib/compilers/_frompython.ts'])],
|
|
11
|
-
|
|
12
|
-
tseslint.configs.recommendedTypeChecked,
|
|
13
|
-
{
|
|
14
|
-
languageOptions: {
|
|
15
|
-
parserOptions: {
|
|
16
|
-
projectService: true,
|
|
17
|
-
tsconfigRootDir: import.meta.dirname,
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
rules: {
|
|
23
|
-
'@typescript-eslint/no-explicit-any': 'off',
|
|
24
|
-
'@typescript-eslint/no-unsafe-assignment': 'off',
|
|
25
|
-
'@typescript-eslint/no-unused-vars': 'off',
|
|
26
|
-
'@typescript-eslint/no-unsafe-member-access': 'off',
|
|
27
|
-
// '@typescript-eslint/no-unsafe-return': 'off',
|
|
28
|
-
// '@typescript-eslint/no-unsafe-argument': 'off',
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
)
|
package/lib/ai.ts
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { encode } from 'gpt-tokenizer'
|
|
2
|
-
import { estimateCost } from 'gpt-tokenizer/model/gpt-5'
|
|
3
|
-
import { igniteModel, LlmModel, loadModels, logger, Message } from 'multi-llm-ts'
|
|
4
|
-
|
|
5
|
-
// do not log anything from multi-llm-ts
|
|
6
|
-
logger.disable()
|
|
7
|
-
|
|
8
|
-
export async function complete(model: string, systemPrompt: string, userPrompt: string): Promise<string> {
|
|
9
|
-
const parts = model.split('/')
|
|
10
|
-
const providerName = parts[0]!
|
|
11
|
-
const modelName = parts[1]!
|
|
12
|
-
|
|
13
|
-
const config = { apiKey: process.env[keys[providerName]!] || '' }
|
|
14
|
-
|
|
15
|
-
const models = await loadModels(providerName, config)
|
|
16
|
-
// console.log(models)
|
|
17
|
-
const chat = models!.chat.find((m) => m.id === modelName)!
|
|
18
|
-
|
|
19
|
-
const bot = igniteModel(providerName, chat, config)
|
|
20
|
-
const messages = [new Message('system', systemPrompt), new Message('user', userPrompt)]
|
|
21
|
-
const response = await bot.complete(messages)
|
|
22
|
-
return response.content!
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
type ModelInfo = Record<string, Record<string, string[]>>
|
|
26
|
-
|
|
27
|
-
export async function listModels(): Promise<ModelInfo> {
|
|
28
|
-
const result: ModelInfo = {}
|
|
29
|
-
const providers = Object.keys(keys).sort()
|
|
30
|
-
for (const providerName of providers) {
|
|
31
|
-
if (providerName === 'ollama') continue // Ollama models are local, skip for now
|
|
32
|
-
const config = { apiKey: process.env[keys[providerName]!] || '' }
|
|
33
|
-
const models = await loadModels(providerName, config)
|
|
34
|
-
if (models === null) continue
|
|
35
|
-
result[providerName] = {}
|
|
36
|
-
for (const modelType in models) {
|
|
37
|
-
result[providerName][modelType] = []
|
|
38
|
-
for (const model of models.chat) {
|
|
39
|
-
result[providerName][modelType].push(model.id)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return result
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export class ChatBot {
|
|
47
|
-
private model: string
|
|
48
|
-
private bot: LlmModel | null = null
|
|
49
|
-
private messages: Message[]
|
|
50
|
-
public totalInputTokens: number = 0
|
|
51
|
-
public totalOutputTokens: number = 0
|
|
52
|
-
public totalInputCost: number = 0
|
|
53
|
-
public totalOutputCost: number = 0
|
|
54
|
-
|
|
55
|
-
constructor(model: string, systemPrompt: string) {
|
|
56
|
-
this.model = model
|
|
57
|
-
this.messages = [new Message('system', systemPrompt)]
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async init() {
|
|
61
|
-
const parts = this.model.split('/')
|
|
62
|
-
const providerName = parts[0]!
|
|
63
|
-
const modelName = parts[1]!
|
|
64
|
-
const config = { apiKey: process.env[keys[providerName]!] || '' }
|
|
65
|
-
const models = await loadModels(providerName, config)
|
|
66
|
-
const chat = models!.chat.find((m: any) => m.id === modelName)!
|
|
67
|
-
this.bot = igniteModel(providerName, chat, config)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async complete(userPrompt: string): Promise<string> {
|
|
71
|
-
if (!this.bot) {
|
|
72
|
-
throw new Error(`Model '${this.model}' could not be initialized`)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
this.messages.push(new Message('user', userPrompt))
|
|
76
|
-
const response = await this.bot.complete(this.messages)
|
|
77
|
-
this.messages.push(new Message('assistant', response.content))
|
|
78
|
-
|
|
79
|
-
const inputTokens = encode(userPrompt).length
|
|
80
|
-
const outputTokens = encode(response.content!).length
|
|
81
|
-
const inputCost = estimateCost(inputTokens)
|
|
82
|
-
const outputCost = estimateCost(outputTokens)
|
|
83
|
-
|
|
84
|
-
this.totalInputTokens += inputTokens
|
|
85
|
-
this.totalOutputTokens += outputTokens
|
|
86
|
-
this.totalInputCost += inputCost.main!.input!
|
|
87
|
-
this.totalOutputCost += outputCost.main!.output!
|
|
88
|
-
|
|
89
|
-
return response.content!
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
forgetLastInteraction() {
|
|
93
|
-
if (this.messages.length > 2) {
|
|
94
|
-
this.messages.pop()
|
|
95
|
-
this.messages.pop()
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
modelInformation(): any {
|
|
100
|
-
if (!this.bot) {
|
|
101
|
-
throw new Error(`Model '${this.model}' has not been initialized`)
|
|
102
|
-
}
|
|
103
|
-
return this.bot.model
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export type PowerEstimation = {
|
|
108
|
-
wattHours: number
|
|
109
|
-
joules: number
|
|
110
|
-
co2Grams: number
|
|
111
|
-
trees: number
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export function estimatePowerConsumption(inputTokens: number, outputTokens: number): PowerEstimation {
|
|
115
|
-
// Written by Claude.ai
|
|
116
|
-
|
|
117
|
-
// Very rough estimates based on public data
|
|
118
|
-
// One tree absorbs approximately:
|
|
119
|
-
// - 22 kg (22,000g) of CO2 per year on average
|
|
120
|
-
// - 1 ton (1,000,000g) over its lifetime (~40 years)
|
|
121
|
-
|
|
122
|
-
const wattsPerToken = 0.0003 // ~0.3 milliwatts per token
|
|
123
|
-
const totalTokens = inputTokens + outputTokens
|
|
124
|
-
const co2PerTree = 22000
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
wattHours: (totalTokens * wattsPerToken) / 3600, // Convert to Wh
|
|
128
|
-
joules: totalTokens * wattsPerToken,
|
|
129
|
-
co2Grams: ((totalTokens * wattsPerToken) / 3600) * 0.5, // Rough CO2 estimate
|
|
130
|
-
trees: (((totalTokens * wattsPerToken) / 3600) * 0.5) / co2PerTree,
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const keys: Record<string, string> = {
|
|
135
|
-
google: 'GEMINI_API_KEY',
|
|
136
|
-
openai: 'OPENAI_API_KEY',
|
|
137
|
-
ollama: '',
|
|
138
|
-
}
|
package/lib/assets.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import zipFile from '@/assets.zip' with { type: 'file' }
|
|
2
|
-
import chalk from 'chalk'
|
|
3
|
-
import { unzipSync } from 'fflate'
|
|
4
|
-
import { mkdir, rm } from 'fs/promises'
|
|
5
|
-
import { join } from 'path'
|
|
6
|
-
import { paths } from './settings'
|
|
7
|
-
import * as tui from './tui.js'
|
|
8
|
-
|
|
9
|
-
export async function decompressAssets() {
|
|
10
|
-
await tui.section('Decompressing assets', async () => {
|
|
11
|
-
const destination = paths.data
|
|
12
|
-
tui.action(`Decompressing assets to ${destination}`)
|
|
13
|
-
const bytes = await Bun.file(zipFile).arrayBuffer()
|
|
14
|
-
const zipBuffer = new Uint8Array(bytes)
|
|
15
|
-
|
|
16
|
-
await rm(destination, { recursive: true, force: true })
|
|
17
|
-
await mkdir(destination, { recursive: true })
|
|
18
|
-
|
|
19
|
-
const unzipped = unzipSync(zipBuffer)
|
|
20
|
-
for (const [filePath, content] of Object.entries(unzipped)) {
|
|
21
|
-
// console.log(chalk.magenta(filePath))
|
|
22
|
-
const fullPath = join(destination, filePath)
|
|
23
|
-
if (filePath.endsWith('/')) {
|
|
24
|
-
await mkdir(fullPath, { recursive: true })
|
|
25
|
-
} else {
|
|
26
|
-
await Bun.write(fullPath, content)
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
tui.success('Assets decompressed')
|
|
30
|
-
})
|
|
31
|
-
}
|
package/lib/cleaner.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { confirm } from '@inquirer/prompts'
|
|
2
|
-
import * as tui from '@/lib/tui.js'
|
|
3
|
-
import { readdir, unlink } from 'fs/promises'
|
|
4
|
-
import { join } from 'path'
|
|
5
|
-
|
|
6
|
-
export async function cleanFiles(force: boolean, directory: string): Promise<void> {
|
|
7
|
-
const patterns = [
|
|
8
|
-
'\\.exe$',
|
|
9
|
-
'\\.cor$',
|
|
10
|
-
'\\.out$',
|
|
11
|
-
'\\.class$',
|
|
12
|
-
'\\.o$',
|
|
13
|
-
'\\.ho$',
|
|
14
|
-
'~$',
|
|
15
|
-
'^problem\\.[a-z][a-z]\\.pdf$',
|
|
16
|
-
'^problem\\.[a-z][a-z]\\.ps$',
|
|
17
|
-
'^a\\.out$',
|
|
18
|
-
]
|
|
19
|
-
const pattern = new RegExp(patterns.join('|'))
|
|
20
|
-
|
|
21
|
-
const entries = await readdir(directory, { withFileTypes: true })
|
|
22
|
-
|
|
23
|
-
const removalList: string[] = []
|
|
24
|
-
for (const entry of entries) {
|
|
25
|
-
const fullPath = join(directory, entry.name)
|
|
26
|
-
if (entry.isFile() && pattern.test(entry.name)) {
|
|
27
|
-
removalList.push(fullPath)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (removalList.length === 0) {
|
|
32
|
-
tui.success('No files to remove')
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
tui.warning(`The following ${removalList.length} files will be removed:`)
|
|
37
|
-
for (const elem of removalList.sort()) {
|
|
38
|
-
tui.print(elem)
|
|
39
|
-
}
|
|
40
|
-
console.log()
|
|
41
|
-
|
|
42
|
-
if (!force) {
|
|
43
|
-
const conformation = await confirm({ message: `Remove ${removalList.length} files?`, default: false })
|
|
44
|
-
if (!conformation) return
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
let removalCount = 0
|
|
48
|
-
for (const elem of removalList) {
|
|
49
|
-
try {
|
|
50
|
-
await unlink(elem)
|
|
51
|
-
removalCount++
|
|
52
|
-
} catch (error) {
|
|
53
|
-
tui.error(`Could not remove file ${elem}`)
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
tui.success(`Removed ${removalCount} files`)
|
|
58
|
-
}
|