@puzzmo/cli 1.0.32 → 1.0.34
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/lib/agents/claude.d.ts +5 -0
- package/lib/agents/claude.js +69 -0
- package/lib/agents/claude.js.map +1 -0
- package/lib/agents/codex.d.ts +4 -0
- package/lib/agents/codex.js +90 -0
- package/lib/agents/codex.js.map +1 -0
- package/lib/agents/copilot.d.ts +4 -0
- package/lib/agents/copilot.js +123 -0
- package/lib/agents/copilot.js.map +1 -0
- package/lib/agents/gemini.d.ts +5 -0
- package/lib/agents/gemini.js +69 -0
- package/lib/agents/gemini.js.map +1 -0
- package/lib/agents/index.d.ts +5 -0
- package/lib/agents/index.js +15 -0
- package/lib/agents/index.js.map +1 -0
- package/lib/agents/opencode.d.ts +5 -0
- package/lib/agents/opencode.js +159 -0
- package/lib/agents/opencode.js.map +1 -0
- package/lib/agents/stream-json-cli.d.ts +12 -0
- package/lib/agents/stream-json-cli.js +75 -0
- package/lib/agents/stream-json-cli.js.map +1 -0
- package/lib/agents/types.d.ts +37 -0
- package/lib/agents/types.js +2 -0
- package/lib/agents/types.js.map +1 -0
- package/lib/commands/agent-test.d.ts +3 -0
- package/lib/commands/agent-test.js +79 -0
- package/lib/commands/agent-test.js.map +1 -0
- package/lib/commands/game/create.js +6 -4
- package/lib/commands/game/create.js.map +1 -1
- package/lib/commands/upload.d.ts +5 -1
- package/lib/commands/upload.js +41 -40
- package/lib/commands/upload.js.map +1 -1
- package/lib/index.js +10 -4
- package/lib/index.js.map +1 -1
- package/lib/tui/pipeline.d.ts +1 -2
- package/lib/tui/pipeline.js +160 -267
- package/lib/tui/pipeline.js.map +1 -1
- package/lib/util/api.d.ts +5 -1
- package/lib/util/api.js +29 -16
- package/lib/util/api.js.map +1 -1
- package/lib/util/validatePuzzmoFile.js +31 -6
- package/lib/util/validatePuzzmoFile.js.map +1 -1
- package/package.json +4 -1
- package/src/agents/claude.ts +72 -0
- package/src/agents/codex.ts +82 -0
- package/src/agents/copilot.ts +118 -0
- package/src/agents/gemini.ts +74 -0
- package/src/agents/index.ts +20 -0
- package/src/agents/opencode.ts +154 -0
- package/src/agents/stream-json-cli.ts +86 -0
- package/src/agents/types.ts +22 -0
- package/src/commands/agent-test.ts +81 -0
- package/src/commands/game/create.ts +6 -4
- package/src/commands/upload.ts +53 -39
- package/src/index.ts +10 -4
- package/src/tui/pipeline.ts +151 -268
- package/src/util/api.ts +29 -13
- package/src/util/validatePuzzmoFile.ts +30 -7
package/src/util/api.ts
CHANGED
|
@@ -22,39 +22,49 @@ type CompleteResponse = { assetsBase: string; versionID: string; error?: string
|
|
|
22
22
|
/** Callback for reporting batch upload progress */
|
|
23
23
|
export type UploadProgress = (batch: number, totalBatches: number, uploaded: number) => void
|
|
24
24
|
|
|
25
|
+
/** Options for uploadFiles */
|
|
26
|
+
export type UploadFilesOptions = {
|
|
27
|
+
verbose?: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
/** Wraps fetch to surface the underlying network cause (DNS, ECONNREFUSED, TLS, etc.) */
|
|
26
|
-
const fetchWithContext = async (url: string, init: RequestInit, step: string): Promise<Response> => {
|
|
31
|
+
const fetchWithContext = async (url: string, init: RequestInit, step: string, verbose: boolean): Promise<Response> => {
|
|
32
|
+
if (verbose) console.log(` → ${init.method ?? "GET"} ${url} [${step}]`)
|
|
27
33
|
try {
|
|
28
|
-
|
|
34
|
+
const res = await fetch(url, init)
|
|
35
|
+
if (verbose) console.log(` ← ${res.status} ${res.statusText} [${step}]`)
|
|
36
|
+
return res
|
|
29
37
|
} catch (e) {
|
|
30
38
|
const cause = (e as { cause?: unknown }).cause
|
|
31
39
|
const causeMsg = cause instanceof Error ? cause.message : cause ? String(cause) : (e as Error).message
|
|
40
|
+
if (verbose && e instanceof Error && e.stack) console.error(e.stack)
|
|
32
41
|
throw new Error(`Network error during ${step} (${url}): ${causeMsg}`)
|
|
33
42
|
}
|
|
34
43
|
}
|
|
35
44
|
|
|
36
45
|
/** Reads a response body, parsing JSON when possible and including status + body in errors */
|
|
37
|
-
const readResponse = async (res: Response, url: string, step: string) => {
|
|
46
|
+
const readResponse = async (res: Response, url: string, step: string, verbose: boolean) => {
|
|
38
47
|
const text = await res.text()
|
|
39
48
|
let json: unknown
|
|
40
49
|
try {
|
|
41
50
|
json = text ? JSON.parse(text) : {}
|
|
42
51
|
} catch {
|
|
43
52
|
if (!res.ok) {
|
|
44
|
-
const snippet = text.slice(0, 200).replace(/\s+/g, " ").trim()
|
|
53
|
+
const snippet = verbose ? text : text.slice(0, 200).replace(/\s+/g, " ").trim()
|
|
45
54
|
throw new Error(`Server error during ${step} (${res.status} ${res.statusText} from ${url}): ${snippet || "no body"}`)
|
|
46
55
|
}
|
|
47
|
-
throw new Error(`Invalid JSON response during ${step} (${url}): ${text.slice(0, 200)}`)
|
|
56
|
+
throw new Error(`Invalid JSON response during ${step} (${url}): ${verbose ? text : text.slice(0, 200)}`)
|
|
48
57
|
}
|
|
49
58
|
if (!res.ok) {
|
|
50
59
|
const serverMsg = (json as { error?: string }).error || res.statusText || "Unknown error"
|
|
51
|
-
|
|
60
|
+
const detail = verbose ? `\n${JSON.stringify(json, null, 2)}` : ""
|
|
61
|
+
throw new Error(`Server error during ${step} (${res.status} from ${url}): ${serverMsg}${detail}`)
|
|
52
62
|
}
|
|
53
63
|
return json
|
|
54
64
|
}
|
|
55
65
|
|
|
56
66
|
/** Sends a JSON POST request */
|
|
57
|
-
const jsonPost = async (url: string, token: string, body: object, step: string) => {
|
|
67
|
+
const jsonPost = async (url: string, token: string, body: object, step: string, verbose: boolean) => {
|
|
58
68
|
const res = await fetchWithContext(
|
|
59
69
|
url,
|
|
60
70
|
{
|
|
@@ -63,12 +73,13 @@ const jsonPost = async (url: string, token: string, body: object, step: string)
|
|
|
63
73
|
body: JSON.stringify(body),
|
|
64
74
|
},
|
|
65
75
|
step,
|
|
76
|
+
verbose,
|
|
66
77
|
)
|
|
67
|
-
return readResponse(res, url, step)
|
|
78
|
+
return readResponse(res, url, step, verbose)
|
|
68
79
|
}
|
|
69
80
|
|
|
70
81
|
/** Uploads a single file as raw binary with filename in query param */
|
|
71
|
-
const uploadFile = async (url: string, token: string, filePath: string, baseDir: string): Promise<FileResponse> => {
|
|
82
|
+
const uploadFile = async (url: string, token: string, filePath: string, baseDir: string, verbose: boolean): Promise<FileResponse> => {
|
|
72
83
|
const relativePath = path.relative(baseDir, filePath)
|
|
73
84
|
const content = fs.readFileSync(filePath)
|
|
74
85
|
const step = `file upload (${relativePath})`
|
|
@@ -85,8 +96,9 @@ const uploadFile = async (url: string, token: string, filePath: string, baseDir:
|
|
|
85
96
|
body: content,
|
|
86
97
|
},
|
|
87
98
|
step,
|
|
99
|
+
verbose,
|
|
88
100
|
)
|
|
89
|
-
return (await readResponse(res, fullURL, step)) as FileResponse
|
|
101
|
+
return (await readResponse(res, fullURL, step, verbose)) as FileResponse
|
|
90
102
|
}
|
|
91
103
|
|
|
92
104
|
/** Multi-step upload: init -> batched file uploads -> complete */
|
|
@@ -98,11 +110,15 @@ export const uploadFiles = async (
|
|
|
98
110
|
baseDir: string,
|
|
99
111
|
puzzmoFile: PuzzmoFile,
|
|
100
112
|
onProgress?: UploadProgress,
|
|
113
|
+
options: UploadFilesOptions = {},
|
|
101
114
|
): Promise<CompleteResponse> => {
|
|
115
|
+
const { verbose = false } = options
|
|
102
116
|
const apiURL = getAPIURL()
|
|
117
|
+
if (verbose) console.log(` API URL: ${apiURL}`)
|
|
103
118
|
|
|
104
119
|
// Step 1: Init session (includes puzzmo.json metadata)
|
|
105
|
-
const init = (await jsonPost(`${apiURL}/cliUpload`, token, { gameSlug, sha, puzzmoFile }, "upload init")) as InitResponse
|
|
120
|
+
const init = (await jsonPost(`${apiURL}/cliUpload`, token, { gameSlug, sha, puzzmoFile }, "upload init", verbose)) as InitResponse
|
|
121
|
+
if (verbose) console.log(` session: ${init.sessionID}`)
|
|
106
122
|
|
|
107
123
|
// Step 2: Upload files in concurrent batches
|
|
108
124
|
const fileURL = `${apiURL}/cliUpload/${init.sessionID}/file`
|
|
@@ -112,11 +128,11 @@ export const uploadFiles = async (
|
|
|
112
128
|
for (let i = 0; i < filePaths.length; i += BATCH_SIZE) {
|
|
113
129
|
const batch = filePaths.slice(i, i + BATCH_SIZE)
|
|
114
130
|
const batchNum = Math.floor(i / BATCH_SIZE) + 1
|
|
115
|
-
await Promise.all(batch.map((fp) => uploadFile(fileURL, token, fp, baseDir)))
|
|
131
|
+
await Promise.all(batch.map((fp) => uploadFile(fileURL, token, fp, baseDir, verbose)))
|
|
116
132
|
totalUploaded += batch.length
|
|
117
133
|
onProgress?.(batchNum, totalBatches, totalUploaded)
|
|
118
134
|
}
|
|
119
135
|
|
|
120
136
|
// Step 3: Complete
|
|
121
|
-
return (await jsonPost(`${apiURL}/cliUpload/${init.sessionID}/complete`, token, {}, "upload complete")) as CompleteResponse
|
|
137
|
+
return (await jsonPost(`${apiURL}/cliUpload/${init.sessionID}/complete`, token, {}, "upload complete", verbose)) as CompleteResponse
|
|
122
138
|
}
|
|
@@ -1,18 +1,32 @@
|
|
|
1
|
-
import { Validator } from "@cfworker/json-schema"
|
|
1
|
+
import { Validator, type Schema } from "@cfworker/json-schema"
|
|
2
2
|
|
|
3
3
|
import type { PuzzmoFile } from "./api.js"
|
|
4
4
|
|
|
5
|
-
const schemaURL = "https://dev.puzzmo.com/schema/puzzmo-file-schema.json"
|
|
5
|
+
const schemaURL = "https://dev-dj9e.onrender.com/schema/puzzmo-file-schema.json" //"https://dev.puzzmo.com/schema/puzzmo-file-schema.json"
|
|
6
6
|
|
|
7
7
|
let cachedValidator: Validator | undefined
|
|
8
8
|
|
|
9
9
|
/** Fetches and caches a JSON schema validator for puzzmo.json files */
|
|
10
10
|
const getValidator = async () => {
|
|
11
11
|
if (cachedValidator) return cachedValidator
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
let res: Response
|
|
13
|
+
try {
|
|
14
|
+
res = await fetch(schemaURL)
|
|
15
|
+
} catch (e) {
|
|
16
|
+
const cause = (e as { cause?: unknown }).cause
|
|
17
|
+
const causeMsg = cause instanceof Error ? cause.message : cause ? String(cause) : (e as Error).message
|
|
18
|
+
throw new Error(`Network error fetching puzzmo file schema (${schemaURL}): ${causeMsg}`)
|
|
19
|
+
}
|
|
20
|
+
if (!res.ok) {
|
|
21
|
+
throw new Error(`Failed to fetch puzzmo file schema: ${res.status} ${res.statusText} from ${schemaURL}`)
|
|
22
|
+
}
|
|
23
|
+
let puzzmoSchema: unknown
|
|
24
|
+
try {
|
|
25
|
+
puzzmoSchema = await res.json()
|
|
26
|
+
} catch (e) {
|
|
27
|
+
throw new Error(`Invalid JSON in puzzmo file schema (${schemaURL}): ${e instanceof Error ? e.message : e}`)
|
|
28
|
+
}
|
|
29
|
+
cachedValidator = new Validator(puzzmoSchema as Schema, "7")
|
|
16
30
|
return cachedValidator
|
|
17
31
|
}
|
|
18
32
|
|
|
@@ -21,7 +35,8 @@ type ValidationResult = { valid: true; data: PuzzmoFile } | { valid: false; erro
|
|
|
21
35
|
/** Validates a parsed object against the puzzmo.json JSON schema */
|
|
22
36
|
export const validatePuzzmoJson = async (data: unknown): Promise<ValidationResult> => {
|
|
23
37
|
const validator = await getValidator()
|
|
24
|
-
const
|
|
38
|
+
const toValidate = stripSchemaProperty(data)
|
|
39
|
+
const result = validator.validate(toValidate)
|
|
25
40
|
if (result.valid) return { valid: true, data: data as PuzzmoFile }
|
|
26
41
|
return {
|
|
27
42
|
valid: false,
|
|
@@ -30,3 +45,11 @@ export const validatePuzzmoJson = async (data: unknown): Promise<ValidationResul
|
|
|
30
45
|
.map((e) => `${e.instanceLocation}: ${e.error}`),
|
|
31
46
|
}
|
|
32
47
|
}
|
|
48
|
+
|
|
49
|
+
/** Removes the `$schema` editor-hint property so it doesn't trip additionalProperties checks */
|
|
50
|
+
const stripSchemaProperty = (data: unknown): unknown => {
|
|
51
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return data
|
|
52
|
+
if (!("$schema" in data)) return data
|
|
53
|
+
const { $schema: _, ...rest } = data as Record<string, unknown>
|
|
54
|
+
return rest
|
|
55
|
+
}
|