dsh-plugin-discourse 0.0.0-stage → 1.0.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/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Discourse",
4
+ "description": "Search, read, post, moderate, and administer a Discourse forum."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Discourse 论坛",
4
+ "description": "搜索、阅读、发帖、审核与管理 Discourse 论坛。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,6 +1,52 @@
1
1
  {
2
2
  "name": "dsh-plugin-discourse",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Discourse administration and agent tools for the DeepSeek Harness: 16 tools for reading, posting, moderating, and administering a Discourse forum.",
5
+ "license": "MIT",
6
+ "author": "AkarinLiu",
7
+ "type": "module",
8
+ "engines": {
9
+ "node": ">=20"
10
+ },
11
+ "keywords": [
12
+ "dsh",
13
+ "deepseek-harness",
14
+ "discourse",
15
+ "forum",
16
+ "plugin",
17
+ "agent-tools",
18
+ "moderation",
19
+ "chatops"
20
+ ],
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/AkarinLiu/dsh-plugin-discourse.git"
24
+ },
25
+ "homepage": "https://github.com/AkarinLiu/dsh-plugin-discourse#readme",
26
+ "bugs": {
27
+ "url": "https://github.com/AkarinLiu/dsh-plugin-discourse/issues"
28
+ },
29
+ "publishConfig": {
30
+ "registry": "https://registry.npmjs.org",
31
+ "access": "public"
32
+ },
33
+ "exports": {
34
+ ".": "./index.js",
35
+ "./locale/*.json": "./locale/*.json",
36
+ "./icon": "./icon.svg"
37
+ },
38
+ "files": [
39
+ "index.js",
40
+ "cordis.patch.yml",
41
+ "README.md",
42
+ "LICENSE",
43
+ "locale/*.json",
44
+ "icon.svg",
45
+ "scripts/"
46
+ ],
47
+ "dsh": {
48
+ "bundle": {
49
+ "patch": "./cordis.patch.yml"
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Offline and live smoke test for the Discourse plugin.
4
+ *
5
+ * The stub context mirrors what the Loader provides to `apply`, so this
6
+ * exercises the real registration path without booting a Harness.
7
+ *
8
+ * node scripts/smoke.mjs definition checks only
9
+ * node scripts/smoke.mjs --live also call read-only tools against the forum
10
+ */
11
+
12
+ import { apply } from '../index.js'
13
+
14
+ const live = process.argv.includes('--live')
15
+ const baseUrl = process.env.DISCOURSE_BASE_URL ?? 'https://meta.discourse.org'
16
+ const registry = new Map()
17
+ const problems = []
18
+
19
+ const ctx = {
20
+ tools: {
21
+ register(definition) {
22
+ if (registry.has(definition.name)) problems.push(`duplicate tool name ${definition.name}`)
23
+ registry.set(definition.name, definition)
24
+ return () => registry.delete(definition.name)
25
+ },
26
+ },
27
+ effect(callback, label) {
28
+ const dispose = callback()
29
+ return () => {
30
+ if (typeof dispose === 'function') dispose()
31
+ }
32
+ },
33
+ }
34
+
35
+ apply(ctx, { baseUrl, readOnly: true, maxItems: 5 })
36
+
37
+ const expected = [
38
+ 'discourse_search',
39
+ 'discourse_topics',
40
+ 'discourse_topic',
41
+ 'discourse_categories',
42
+ 'discourse_user',
43
+ 'discourse_create_topic',
44
+ 'discourse_reply',
45
+ 'discourse_update_post',
46
+ 'discourse_flag_post',
47
+ 'discourse_review_queue',
48
+ 'discourse_delete_post',
49
+ 'discourse_restore_post',
50
+ 'discourse_admin_users',
51
+ 'discourse_moderate_user',
52
+ 'discourse_site_settings',
53
+ 'discourse_admin_stats',
54
+ ]
55
+
56
+ for (const name of expected) {
57
+ if (!registry.has(name)) problems.push(`missing tool ${name}`)
58
+ }
59
+ for (const name of registry.keys()) {
60
+ if (!expected.includes(name)) problems.push(`unexpected tool ${name}`)
61
+ }
62
+
63
+ for (const [name, tool] of registry) {
64
+ if (!/^discourse_[a-z_]+$/.test(name)) problems.push(`${name}: name must match discourse_[a-z_]+`)
65
+ if (typeof tool.description !== 'string' || tool.description.length < 40) problems.push(`${name}: description too short`)
66
+ if (tool.parameters?.type !== 'object') problems.push(`${name}: parameters must be an object schema`)
67
+ if (tool.parameters?.additionalProperties !== false) problems.push(`${name}: parameters must set additionalProperties false`)
68
+ if (typeof tool.output?.render !== 'function') problems.push(`${name}: output.render missing`)
69
+ if (tool.output?.schema?.type !== 'object') problems.push(`${name}: output schema must be an object`)
70
+ if (typeof tool.execute !== 'function') problems.push(`${name}: execute missing`)
71
+ for (const [key, schema] of Object.entries(tool.parameters?.properties ?? {})) {
72
+ if (typeof schema.description !== 'string' || schema.description.length === 0) problems.push(`${name}.${key}: parameter needs a description`)
73
+ if (schema.type === undefined && schema.oneOf === undefined) problems.push(`${name}.${key}: parameter needs a type`)
74
+ }
75
+ }
76
+
77
+ const describe = (value) => {
78
+ const rendered = value.map((block) => block.text ?? `[${block.type}]`).join('\n')
79
+ return rendered.length > 700 ? `${rendered.slice(0, 700)}…` : rendered
80
+ }
81
+
82
+ if (live) {
83
+ const cases = [
84
+ ['discourse_categories', {}],
85
+ ['discourse_topics', { filter: 'latest', limit: 3 }],
86
+ ['discourse_search', { q: 'api', limit: 3 }],
87
+ ['discourse_topic', { topic: 1, limit: 2 }],
88
+ ['discourse_user', { username: 'system' }],
89
+ ]
90
+ for (const [name, args] of cases) {
91
+ try {
92
+ const value = await registry.get(name).execute(args, { signal: AbortSignal.timeout(30000) })
93
+ console.log(`\n=== ${name} ===\n${describe(registry.get(name).output.render(args, value))}`)
94
+ } catch (error) {
95
+ problems.push(`${name}: ${error.message}`)
96
+ }
97
+ }
98
+ }
99
+
100
+ if (problems.length > 0) {
101
+ console.error('smoke test failed:')
102
+ for (const problem of problems) console.error(` - ${problem}`)
103
+ process.exit(1)
104
+ }
105
+
106
+ console.log(`ok: ${registry.size} tools registered${live ? ' (live calls included)' : ''}`)