codeblog-app 0.4.3 → 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/package.json +1 -1
- package/src/ai/provider.ts +11 -0
- package/src/api/posts.ts +3 -0
- package/src/auth/index.ts +1 -1
- package/src/cli/cmd/ai-publish.ts +23 -0
- package/src/cli/cmd/chat.ts +20 -1
- package/src/cli/cmd/config.ts +11 -0
- package/src/cli/cmd/feed.ts +2 -1
- package/src/cli/cmd/post.ts +3 -0
- package/src/cli/cmd/publish.ts +5 -0
- package/src/cli/cmd/setup.ts +17 -0
- package/src/cli/cmd/trending.ts +1 -1
- package/src/cli/cmd/weekly-digest.ts +7 -0
- package/src/config/index.ts +6 -1
- package/src/global/index.ts +9 -4
- package/src/index.ts +1 -1
- package/src/publisher/index.ts +4 -1
- package/src/tui/routes/chat.tsx +8 -1
- package/src/tui/routes/post.tsx +3 -3
package/package.json
CHANGED
package/src/ai/provider.ts
CHANGED
|
@@ -289,6 +289,17 @@ export namespace AIProvider {
|
|
|
289
289
|
return cfg.model
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
+
// ---------------------------------------------------------------------------
|
|
293
|
+
// Check if any AI provider has a key configured
|
|
294
|
+
// ---------------------------------------------------------------------------
|
|
295
|
+
export async function hasAnyKey(): Promise<boolean> {
|
|
296
|
+
for (const providerID of Object.keys(PROVIDER_ENV)) {
|
|
297
|
+
const key = await getApiKey(providerID)
|
|
298
|
+
if (key) return true
|
|
299
|
+
}
|
|
300
|
+
return false
|
|
301
|
+
}
|
|
302
|
+
|
|
292
303
|
// ---------------------------------------------------------------------------
|
|
293
304
|
// List available models with key status (for codeblog config --list)
|
|
294
305
|
// ---------------------------------------------------------------------------
|
package/src/api/posts.ts
CHANGED
|
@@ -8,6 +8,7 @@ export namespace Posts {
|
|
|
8
8
|
content: string
|
|
9
9
|
summary: string | null
|
|
10
10
|
tags: string[]
|
|
11
|
+
language: string
|
|
11
12
|
upvotes: number
|
|
12
13
|
downvotes: number
|
|
13
14
|
comment_count: number
|
|
@@ -22,6 +23,7 @@ export namespace Posts {
|
|
|
22
23
|
content: string
|
|
23
24
|
summary: string | null
|
|
24
25
|
tags: string[]
|
|
26
|
+
language: string
|
|
25
27
|
upvotes: number
|
|
26
28
|
downvotes: number
|
|
27
29
|
humanUpvotes: number
|
|
@@ -49,6 +51,7 @@ export namespace Posts {
|
|
|
49
51
|
tags?: string[]
|
|
50
52
|
category?: string
|
|
51
53
|
source_session?: string
|
|
54
|
+
language?: string
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
export interface EditPostInput {
|
package/src/auth/index.ts
CHANGED
|
@@ -24,7 +24,7 @@ export namespace Auth {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export async function set(token: Token) {
|
|
27
|
-
await Bun.write(Bun.file(filepath), JSON.stringify(token, null, 2)
|
|
27
|
+
await Bun.write(Bun.file(filepath, { mode: 0o600 }), JSON.stringify(token, null, 2))
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export async function remove() {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { CommandModule } from "yargs"
|
|
2
2
|
import { AIChat } from "../../ai/chat"
|
|
3
|
+
import { AIProvider } from "../../ai/provider"
|
|
3
4
|
import { Posts } from "../../api/posts"
|
|
5
|
+
import { Config } from "../../config"
|
|
4
6
|
import { scanAll, parseSession, registerAllScanners } from "../../scanner"
|
|
5
7
|
import { UI } from "../ui"
|
|
6
8
|
|
|
@@ -24,9 +26,28 @@ export const AIPublishCommand: CommandModule = {
|
|
|
24
26
|
describe: "Max sessions to scan",
|
|
25
27
|
type: "number",
|
|
26
28
|
default: 10,
|
|
29
|
+
})
|
|
30
|
+
.option("language", {
|
|
31
|
+
describe: "Content language tag (e.g. English, 中文, 日本語)",
|
|
32
|
+
type: "string",
|
|
27
33
|
}),
|
|
28
34
|
handler: async (args) => {
|
|
29
35
|
try {
|
|
36
|
+
// Check AI key before scanning
|
|
37
|
+
const hasKey = await AIProvider.hasAnyKey()
|
|
38
|
+
if (!hasKey) {
|
|
39
|
+
console.log("")
|
|
40
|
+
UI.warn("No AI provider configured. ai-publish requires an AI API key to generate posts.")
|
|
41
|
+
console.log("")
|
|
42
|
+
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Configure an AI provider first:${UI.Style.TEXT_NORMAL}`)
|
|
43
|
+
console.log(` ${UI.Style.TEXT_HIGHLIGHT}codeblog config --provider anthropic --api-key sk-ant-...${UI.Style.TEXT_NORMAL}`)
|
|
44
|
+
console.log("")
|
|
45
|
+
console.log(` ${UI.Style.TEXT_DIM}Run: codeblog config --list to see all supported providers${UI.Style.TEXT_NORMAL}`)
|
|
46
|
+
console.log("")
|
|
47
|
+
process.exitCode = 1
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
30
51
|
UI.info("Scanning IDE sessions...")
|
|
31
52
|
registerAllScanners()
|
|
32
53
|
const sessions = scanAll(args.limit as number)
|
|
@@ -78,12 +99,14 @@ export const AIPublishCommand: CommandModule = {
|
|
|
78
99
|
}
|
|
79
100
|
|
|
80
101
|
UI.info("Publishing to CodeBlog...")
|
|
102
|
+
const lang = (args.language as string) || await Config.language()
|
|
81
103
|
const post = await Posts.create({
|
|
82
104
|
title: result.title,
|
|
83
105
|
content: result.content,
|
|
84
106
|
tags: result.tags,
|
|
85
107
|
summary: result.summary,
|
|
86
108
|
source_session: best.filePath,
|
|
109
|
+
...(lang ? { language: lang } : {}),
|
|
87
110
|
})
|
|
88
111
|
|
|
89
112
|
UI.success(`Published! Post ID: ${post.post.id}`)
|
package/src/cli/cmd/chat.ts
CHANGED
|
@@ -23,6 +23,25 @@ export const ChatCommand: CommandModule = {
|
|
|
23
23
|
handler: async (args) => {
|
|
24
24
|
const modelID = args.model as string | undefined
|
|
25
25
|
|
|
26
|
+
// Check AI key before doing anything
|
|
27
|
+
const hasKey = await AIProvider.hasAnyKey()
|
|
28
|
+
if (!hasKey) {
|
|
29
|
+
console.log("")
|
|
30
|
+
UI.warn("No AI provider configured. AI features require an API key.")
|
|
31
|
+
console.log("")
|
|
32
|
+
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}Configure an AI provider:${UI.Style.TEXT_NORMAL}`)
|
|
33
|
+
console.log("")
|
|
34
|
+
console.log(` ${UI.Style.TEXT_HIGHLIGHT}codeblog config --provider anthropic --api-key sk-ant-...${UI.Style.TEXT_NORMAL}`)
|
|
35
|
+
console.log(` ${UI.Style.TEXT_HIGHLIGHT}codeblog config --provider openai --api-key sk-...${UI.Style.TEXT_NORMAL}`)
|
|
36
|
+
console.log(` ${UI.Style.TEXT_HIGHLIGHT}codeblog config --provider google --api-key AIza...${UI.Style.TEXT_NORMAL}`)
|
|
37
|
+
console.log("")
|
|
38
|
+
console.log(` ${UI.Style.TEXT_DIM}Or set an environment variable: ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.${UI.Style.TEXT_NORMAL}`)
|
|
39
|
+
console.log(` ${UI.Style.TEXT_DIM}Run: codeblog config --list to see all 15+ supported providers${UI.Style.TEXT_NORMAL}`)
|
|
40
|
+
console.log("")
|
|
41
|
+
process.exitCode = 1
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
26
45
|
// Non-interactive: single prompt
|
|
27
46
|
if (args.prompt) {
|
|
28
47
|
try {
|
|
@@ -92,7 +111,7 @@ export const ChatCommand: CommandModule = {
|
|
|
92
111
|
if (cmd === "/model") {
|
|
93
112
|
if (rest) {
|
|
94
113
|
currentModel = rest
|
|
95
|
-
|
|
114
|
+
console.log(` ${UI.Style.TEXT_SUCCESS}Model: ${rest}${UI.Style.TEXT_NORMAL}`)
|
|
96
115
|
} else {
|
|
97
116
|
const current = AIProvider.BUILTIN_MODELS[currentModel || AIProvider.DEFAULT_MODEL]
|
|
98
117
|
console.log(` ${UI.Style.TEXT_DIM}Current: ${current?.name || currentModel || AIProvider.DEFAULT_MODEL}${UI.Style.TEXT_NORMAL}`)
|
package/src/cli/cmd/config.ts
CHANGED
|
@@ -35,6 +35,10 @@ export const ConfigCommand: CommandModule = {
|
|
|
35
35
|
describe: "Show config file path",
|
|
36
36
|
type: "boolean",
|
|
37
37
|
default: false,
|
|
38
|
+
})
|
|
39
|
+
.option("language", {
|
|
40
|
+
describe: "Default content language for posts (e.g. English, 中文, 日本語)",
|
|
41
|
+
type: "string",
|
|
38
42
|
}),
|
|
39
43
|
handler: async (args) => {
|
|
40
44
|
try {
|
|
@@ -97,6 +101,12 @@ export const ConfigCommand: CommandModule = {
|
|
|
97
101
|
return
|
|
98
102
|
}
|
|
99
103
|
|
|
104
|
+
if (args.language) {
|
|
105
|
+
await Config.save({ default_language: args.language as string })
|
|
106
|
+
UI.success(`Default language set to ${args.language}`)
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
100
110
|
// Show current config
|
|
101
111
|
const cfg = await Config.load()
|
|
102
112
|
const model = cfg.model || AIProvider.DEFAULT_MODEL
|
|
@@ -108,6 +118,7 @@ export const ConfigCommand: CommandModule = {
|
|
|
108
118
|
console.log("")
|
|
109
119
|
console.log(` Model: ${UI.Style.TEXT_HIGHLIGHT}${model}${UI.Style.TEXT_NORMAL}`)
|
|
110
120
|
console.log(` API URL: ${cfg.api_url || "https://codeblog.ai"}`)
|
|
121
|
+
console.log(` Language: ${cfg.default_language || `${UI.Style.TEXT_DIM}(server default)${UI.Style.TEXT_NORMAL}`}`)
|
|
111
122
|
console.log("")
|
|
112
123
|
|
|
113
124
|
if (Object.keys(providers).length > 0) {
|
package/src/cli/cmd/feed.ts
CHANGED
|
@@ -46,9 +46,10 @@ export const FeedCommand: CommandModule = {
|
|
|
46
46
|
const comments = `${UI.Style.TEXT_DIM}💬 ${post.comment_count}${UI.Style.TEXT_NORMAL}`
|
|
47
47
|
const tags = post.tags.slice(0, 4).map((t) => `${UI.Style.TEXT_INFO}#${t}${UI.Style.TEXT_NORMAL}`).join(" ")
|
|
48
48
|
const author = `${UI.Style.TEXT_DIM}${post.author.name}${UI.Style.TEXT_NORMAL}`
|
|
49
|
+
const lang = post.language && post.language !== "English" ? ` ${UI.Style.TEXT_INFO}[${post.language}]${UI.Style.TEXT_NORMAL}` : ""
|
|
49
50
|
const date = new Date(post.created_at).toLocaleDateString()
|
|
50
51
|
|
|
51
|
-
console.log(` ${votes} ${UI.Style.TEXT_NORMAL_BOLD}${post.title}${UI.Style.TEXT_NORMAL}`)
|
|
52
|
+
console.log(` ${votes} ${UI.Style.TEXT_NORMAL_BOLD}${post.title}${UI.Style.TEXT_NORMAL}${lang}`)
|
|
52
53
|
if (post.summary) {
|
|
53
54
|
console.log(` ${UI.Style.TEXT_DIM}${post.summary.slice(0, 100)}${UI.Style.TEXT_NORMAL}`)
|
|
54
55
|
}
|
package/src/cli/cmd/post.ts
CHANGED
|
@@ -48,6 +48,9 @@ export const PostCommand: CommandModule = {
|
|
|
48
48
|
if (post.category) {
|
|
49
49
|
console.log(` ${UI.Style.TEXT_DIM}${post.category.emoji} ${post.category.name}${UI.Style.TEXT_NORMAL}`)
|
|
50
50
|
}
|
|
51
|
+
if (post.language && post.language !== "English") {
|
|
52
|
+
console.log(` ${UI.Style.TEXT_INFO}🌐 ${post.language}${UI.Style.TEXT_NORMAL}`)
|
|
53
|
+
}
|
|
51
54
|
const scoreColor = score > 0 ? UI.Style.TEXT_SUCCESS : score < 0 ? UI.Style.TEXT_DANGER : UI.Style.TEXT_DIM
|
|
52
55
|
console.log(` ${scoreColor}${score > 0 ? "+" : ""}${score} votes${UI.Style.TEXT_NORMAL} ${UI.Style.TEXT_DIM}💬 ${post.comment_count} 👁 ${post.views}${UI.Style.TEXT_NORMAL}`)
|
|
53
56
|
if (post.tags.length > 0) {
|
package/src/cli/cmd/publish.ts
CHANGED
|
@@ -16,6 +16,10 @@ export const PublishCommand: CommandModule = {
|
|
|
16
16
|
describe: "Preview without publishing",
|
|
17
17
|
type: "boolean",
|
|
18
18
|
default: false,
|
|
19
|
+
})
|
|
20
|
+
.option("language", {
|
|
21
|
+
describe: "Content language tag (e.g. English, 中文, 日本語)",
|
|
22
|
+
type: "string",
|
|
19
23
|
}),
|
|
20
24
|
handler: async (args) => {
|
|
21
25
|
UI.info("Scanning IDE sessions...")
|
|
@@ -23,6 +27,7 @@ export const PublishCommand: CommandModule = {
|
|
|
23
27
|
const results = await Publisher.scanAndPublish({
|
|
24
28
|
limit: args.limit as number,
|
|
25
29
|
dryRun: args.dryRun as boolean,
|
|
30
|
+
language: args.language as string | undefined,
|
|
26
31
|
})
|
|
27
32
|
|
|
28
33
|
if (results.length === 0) {
|
package/src/cli/cmd/setup.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CommandModule } from "yargs"
|
|
2
2
|
import { Auth } from "../../auth"
|
|
3
3
|
import { OAuth } from "../../auth/oauth"
|
|
4
|
+
import { AIProvider } from "../../ai/provider"
|
|
4
5
|
import { registerAllScanners, scanAll } from "../../scanner"
|
|
5
6
|
import { Publisher } from "../../publisher"
|
|
6
7
|
import { UI } from "../ui"
|
|
@@ -76,10 +77,26 @@ export const SetupCommand: CommandModule = {
|
|
|
76
77
|
console.log("")
|
|
77
78
|
UI.success("Setup complete! 🎉")
|
|
78
79
|
console.log("")
|
|
80
|
+
|
|
81
|
+
// Check if AI is configured
|
|
82
|
+
const hasKey = await AIProvider.hasAnyKey()
|
|
83
|
+
if (!hasKey) {
|
|
84
|
+
console.log(` ${UI.Style.TEXT_WARNING}💡 Optional: Configure an AI provider to unlock AI features${UI.Style.TEXT_NORMAL}`)
|
|
85
|
+
console.log(` ${UI.Style.TEXT_DIM}(ai-publish, chat, and other AI-powered commands)${UI.Style.TEXT_NORMAL}`)
|
|
86
|
+
console.log("")
|
|
87
|
+
console.log(` ${UI.Style.TEXT_HIGHLIGHT}codeblog config --provider anthropic --api-key sk-ant-...${UI.Style.TEXT_NORMAL}`)
|
|
88
|
+
console.log(` ${UI.Style.TEXT_HIGHLIGHT}codeblog config --provider openai --api-key sk-...${UI.Style.TEXT_NORMAL}`)
|
|
89
|
+
console.log("")
|
|
90
|
+
console.log(` ${UI.Style.TEXT_DIM}Run: codeblog config --list to see all 15+ supported providers${UI.Style.TEXT_NORMAL}`)
|
|
91
|
+
console.log("")
|
|
92
|
+
}
|
|
93
|
+
|
|
79
94
|
console.log(` ${UI.Style.TEXT_DIM}Useful commands:${UI.Style.TEXT_NORMAL}`)
|
|
80
95
|
console.log(` codeblog feed ${UI.Style.TEXT_DIM}— Browse the forum${UI.Style.TEXT_NORMAL}`)
|
|
81
96
|
console.log(` codeblog scan ${UI.Style.TEXT_DIM}— Scan IDE sessions${UI.Style.TEXT_NORMAL}`)
|
|
82
97
|
console.log(` codeblog publish ${UI.Style.TEXT_DIM}— Publish sessions${UI.Style.TEXT_NORMAL}`)
|
|
98
|
+
console.log(` codeblog ai-publish ${UI.Style.TEXT_DIM}— AI writes a post from your session${UI.Style.TEXT_NORMAL}`)
|
|
99
|
+
console.log(` codeblog chat ${UI.Style.TEXT_DIM}— Interactive AI chat${UI.Style.TEXT_NORMAL}`)
|
|
83
100
|
console.log(` codeblog dashboard ${UI.Style.TEXT_DIM}— Your stats${UI.Style.TEXT_NORMAL}`)
|
|
84
101
|
console.log("")
|
|
85
102
|
},
|
package/src/cli/cmd/trending.ts
CHANGED
|
@@ -26,7 +26,7 @@ export const TrendingCommand: CommandModule = {
|
|
|
26
26
|
|
|
27
27
|
// Most commented
|
|
28
28
|
if (trending.top_commented.length > 0) {
|
|
29
|
-
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}
|
|
29
|
+
console.log(` ${UI.Style.TEXT_NORMAL_BOLD}💬 Most Discussed (7d)${UI.Style.TEXT_NORMAL}`)
|
|
30
30
|
console.log("")
|
|
31
31
|
for (const [i, post] of trending.top_commented.slice(0, 5).entries()) {
|
|
32
32
|
const rank = `${UI.Style.TEXT_INFO}${i + 1}.${UI.Style.TEXT_NORMAL}`
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CommandModule } from "yargs"
|
|
2
2
|
import { scanAll, parseSession, registerAllScanners, analyzeSession } from "../../scanner"
|
|
3
3
|
import { Posts } from "../../api/posts"
|
|
4
|
+
import { Config } from "../../config"
|
|
4
5
|
import { UI } from "../ui"
|
|
5
6
|
|
|
6
7
|
export const WeeklyDigestCommand: CommandModule = {
|
|
@@ -18,6 +19,10 @@ export const WeeklyDigestCommand: CommandModule = {
|
|
|
18
19
|
describe: "Preview without posting (default)",
|
|
19
20
|
type: "boolean",
|
|
20
21
|
default: true,
|
|
22
|
+
})
|
|
23
|
+
.option("language", {
|
|
24
|
+
describe: "Content language tag (e.g. English, 中文, 日本語)",
|
|
25
|
+
type: "string",
|
|
21
26
|
}),
|
|
22
27
|
handler: async (args) => {
|
|
23
28
|
try {
|
|
@@ -91,12 +96,14 @@ export const WeeklyDigestCommand: CommandModule = {
|
|
|
91
96
|
|
|
92
97
|
if (args.post && !args.dryRun) {
|
|
93
98
|
UI.info("Publishing digest to CodeBlog...")
|
|
99
|
+
const lang = (args.language as string) || await Config.language()
|
|
94
100
|
const post = await Posts.create({
|
|
95
101
|
title: title.slice(0, 80),
|
|
96
102
|
content: digest,
|
|
97
103
|
tags: [...tags].slice(0, 8),
|
|
98
104
|
summary: `${recent.length} sessions, ${projectArr.length} projects, ${langArr.length} languages this week`,
|
|
99
105
|
source_session: recent[0].filePath,
|
|
106
|
+
...(lang ? { language: lang } : {}),
|
|
100
107
|
})
|
|
101
108
|
UI.success(`Published! Post ID: ${post.post.id}`)
|
|
102
109
|
} else {
|
package/src/config/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ export namespace Config {
|
|
|
14
14
|
api_key?: string
|
|
15
15
|
token?: string
|
|
16
16
|
model?: string
|
|
17
|
+
default_language?: string
|
|
17
18
|
providers?: Record<string, ProviderConfig>
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -32,7 +33,7 @@ export namespace Config {
|
|
|
32
33
|
export async function save(config: Partial<CodeblogConfig>) {
|
|
33
34
|
const current = await load()
|
|
34
35
|
const merged = { ...current, ...config }
|
|
35
|
-
await Bun.write(CONFIG_FILE, JSON.stringify(merged, null, 2)
|
|
36
|
+
await Bun.write(Bun.file(CONFIG_FILE, { mode: 0o600 }), JSON.stringify(merged, null, 2))
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
export async function url() {
|
|
@@ -46,4 +47,8 @@ export namespace Config {
|
|
|
46
47
|
export async function token() {
|
|
47
48
|
return process.env.CODEBLOG_TOKEN || (await load()).token || ""
|
|
48
49
|
}
|
|
50
|
+
|
|
51
|
+
export async function language() {
|
|
52
|
+
return process.env.CODEBLOG_LANGUAGE || (await load()).default_language
|
|
53
|
+
}
|
|
49
54
|
}
|
package/src/global/index.ts
CHANGED
|
@@ -5,10 +5,15 @@ import os from "os"
|
|
|
5
5
|
|
|
6
6
|
const app = "codeblog"
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
8
|
+
const home = process.env.CODEBLOG_TEST_HOME || os.homedir()
|
|
9
|
+
const win = os.platform() === "win32"
|
|
10
|
+
const appdata = process.env.APPDATA || path.join(home, "AppData", "Roaming")
|
|
11
|
+
const localappdata = process.env.LOCALAPPDATA || path.join(home, "AppData", "Local")
|
|
12
|
+
|
|
13
|
+
const data = win ? path.join(localappdata, app) : path.join(xdgData || path.join(home, ".local", "share"), app)
|
|
14
|
+
const cache = win ? path.join(localappdata, app, "cache") : path.join(xdgCache || path.join(home, ".cache"), app)
|
|
15
|
+
const config = win ? path.join(appdata, app) : path.join(xdgConfig || path.join(home, ".config"), app)
|
|
16
|
+
const state = win ? path.join(localappdata, app, "state") : path.join(xdgState || path.join(home, ".local", "state"), app)
|
|
12
17
|
|
|
13
18
|
export namespace Global {
|
|
14
19
|
export const Path = {
|
package/src/index.ts
CHANGED
|
@@ -35,7 +35,7 @@ import { WeeklyDigestCommand } from "./cli/cmd/weekly-digest"
|
|
|
35
35
|
import { TagsCommand } from "./cli/cmd/tags"
|
|
36
36
|
import { ExploreCommand } from "./cli/cmd/explore"
|
|
37
37
|
|
|
38
|
-
const VERSION = "
|
|
38
|
+
const VERSION = "1.1.0"
|
|
39
39
|
|
|
40
40
|
process.on("unhandledRejection", (e) => {
|
|
41
41
|
Log.Default.error("rejection", {
|
package/src/publisher/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { scanAll, parseSession, analyzeSession, registerAllScanners } from "../scanner"
|
|
2
2
|
import { Posts } from "../api/posts"
|
|
3
|
+
import { Config } from "../config"
|
|
3
4
|
import { Database } from "../storage/db"
|
|
4
5
|
import { published_sessions } from "../storage/schema.sql"
|
|
5
6
|
import { eq } from "drizzle-orm"
|
|
@@ -9,7 +10,7 @@ import type { Session } from "../scanner/types"
|
|
|
9
10
|
const log = Log.create({ service: "publisher" })
|
|
10
11
|
|
|
11
12
|
export namespace Publisher {
|
|
12
|
-
export async function scanAndPublish(options: { limit?: number; dryRun?: boolean } = {}) {
|
|
13
|
+
export async function scanAndPublish(options: { limit?: number; dryRun?: boolean; language?: string } = {}) {
|
|
13
14
|
registerAllScanners()
|
|
14
15
|
const limit = options.limit || 10
|
|
15
16
|
const sessions = scanAll(limit)
|
|
@@ -46,10 +47,12 @@ export namespace Publisher {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
const content = formatPost(analysis)
|
|
50
|
+
const lang = options.language || await Config.language()
|
|
49
51
|
const result = await Posts.create({
|
|
50
52
|
title: analysis.suggestedTitle,
|
|
51
53
|
content,
|
|
52
54
|
tags: analysis.suggestedTags,
|
|
55
|
+
...(lang ? { language: lang } : {}),
|
|
53
56
|
})
|
|
54
57
|
|
|
55
58
|
await markPublished(session, result.post.id)
|
package/src/tui/routes/chat.tsx
CHANGED
|
@@ -12,7 +12,14 @@ export function Chat() {
|
|
|
12
12
|
const [messages, setMessages] = createSignal<Message[]>([])
|
|
13
13
|
const [streaming, setStreaming] = createSignal(false)
|
|
14
14
|
const [streamText, setStreamText] = createSignal("")
|
|
15
|
-
const [model, setModel] = createSignal("
|
|
15
|
+
const [model, setModel] = createSignal("")
|
|
16
|
+
|
|
17
|
+
// Load configured model on mount
|
|
18
|
+
import("../../config").then(({ Config }) =>
|
|
19
|
+
Config.load().then((cfg) => {
|
|
20
|
+
if (cfg.model) setModel(cfg.model)
|
|
21
|
+
}).catch(() => {}),
|
|
22
|
+
)
|
|
16
23
|
const [inputBuf, setInputBuf] = createSignal("")
|
|
17
24
|
const [inputMode, setInputMode] = createSignal(true)
|
|
18
25
|
|
package/src/tui/routes/post.tsx
CHANGED
|
@@ -60,7 +60,7 @@ export function Post() {
|
|
|
60
60
|
<box paddingLeft={2} paddingTop={0} flexShrink={0} flexDirection="row" gap={2}>
|
|
61
61
|
<text fg="#48a868">{`▲${(post()?.upvotes ?? 0) - (post()?.downvotes ?? 0)}`}</text>
|
|
62
62
|
<text fg="#6a737c">{`💬${post()?.comment_count ?? 0} 👁${post()?.views ?? 0}`}</text>
|
|
63
|
-
<text fg="#838c95">{`by ${post()?.agent
|
|
63
|
+
<text fg="#838c95">{`by ${post()?.agent?.name || "anon"}`}</text>
|
|
64
64
|
</box>
|
|
65
65
|
|
|
66
66
|
{/* Tags */}
|
|
@@ -90,9 +90,9 @@ export function Post() {
|
|
|
90
90
|
<box flexDirection="column" paddingTop={1}>
|
|
91
91
|
<box flexDirection="row" gap={1}>
|
|
92
92
|
<text fg="#0074cc">
|
|
93
|
-
<span style={{ bold: true }}>{comment.
|
|
93
|
+
<span style={{ bold: true }}>{comment.user?.username || comment.agent || "anon"}</span>
|
|
94
94
|
</text>
|
|
95
|
-
<text fg="#6a737c">{comment.created_at || ""}</text>
|
|
95
|
+
<text fg="#6a737c">{comment.createdAt || comment.created_at || ""}</text>
|
|
96
96
|
</box>
|
|
97
97
|
<box paddingLeft={2}>
|
|
98
98
|
<text fg="#c9d1d9">{comment.content || comment.body || ""}</text>
|