@icaruk/zai-peak-hours 0.1.0 → 0.3.1
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/README.md +20 -20
- package/dist/index.js +50 -49
- package/package.json +1 -1
- /package/dist/{peak-hours.d.ts → utils.d.ts} +0 -0
- /package/dist/{peak-hours.js → utils.js} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# @icaruk/zai-peak-hours
|
|
2
2
|
|
|
3
|
-
OpenCode plugin that displays z.ai peak hours information with automatic timezone detection
|
|
3
|
+
OpenCode plugin that displays z.ai peak hours information with automatic timezone detection (UTC+8 / Asia/Shanghai).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🌍 Automatic timezone detection (UTC+8 / Asia/Shanghai)
|
|
8
|
+
- ⏰ Real-time peak hours status (14:00-18:00 UTC+8)
|
|
9
|
+
- 📊 Time remaining until next peak/off-peak transition
|
|
10
|
+
- 💬 Zero-token slash commands (`/peak_hours`, `/peak_hours_status`) - no LLM invocation
|
|
4
11
|
|
|
5
12
|
## Installation
|
|
6
13
|
|
|
@@ -12,32 +19,25 @@ Add to your `~/.config/opencode/opencode.json`:
|
|
|
12
19
|
}
|
|
13
20
|
```
|
|
14
21
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- 🌍 Automatic timezone detection (UTC+8 / Asia/Shanghai)
|
|
18
|
-
- ⏰ Real-time peak hours status (14:00-18:00 UTC+8)
|
|
19
|
-
- 📊 Time remaining until next peak/off-peak transition
|
|
20
|
-
- 🔔 Toast notifications on session start and periodic updates
|
|
21
|
-
- ⚙️ Configurable update interval
|
|
22
|
-
- 🛠️ Manual commands for on-demand status checks
|
|
22
|
+
Restart OpenCode to load the plugin.
|
|
23
23
|
|
|
24
24
|
## Commands
|
|
25
25
|
|
|
26
|
+
Commands are automatically available after plugin installation:
|
|
27
|
+
|
|
26
28
|
- `/peak_hours` - Display current peak hours status
|
|
27
|
-
- `/peak_hours_status` - Display plugin diagnostics and configuration
|
|
29
|
+
- `/peak_hours_status` - Display plugin diagnostics and configuration status
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
These commands execute locally without invoking LLM (zero token usage) via `command.execute.before` hook.
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
## Configuration
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
Currently in peak hours. X hours Y minutes remaining
|
|
36
|
-
```
|
|
35
|
+
Optional configuration file at `~/.config/opencode/peak-hours.json`:
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"enabled": true
|
|
40
|
+
}
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
## Development
|
|
@@ -57,7 +57,7 @@ npm run build
|
|
|
57
57
|
npm run dev
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
Add local config in `opencode.json
|
|
60
|
+
Add local config in `opencode.json`:
|
|
61
61
|
|
|
62
62
|
```json
|
|
63
63
|
{
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { getPeakHoursStatus, formatPeakHoursMessage } from './
|
|
2
|
-
import { DEFAULT_CONFIG } from './config';
|
|
1
|
+
import { getPeakHoursStatus, formatPeakHoursMessage } from './utils.js';
|
|
2
|
+
import { DEFAULT_CONFIG } from './config.js';
|
|
3
3
|
import * as fs from 'node:fs';
|
|
4
4
|
import * as path from 'node:path';
|
|
5
|
+
// Command-handled sentinel
|
|
6
|
+
const COMMAND_HANDLED_SENTINEL = '__PEAK_HOURS_COMMAND_HANDLED__';
|
|
7
|
+
function handled() {
|
|
8
|
+
throw new Error(COMMAND_HANDLED_SENTINEL);
|
|
9
|
+
}
|
|
5
10
|
function getConfigPath() {
|
|
6
11
|
const configDir = process.env.XDG_CONFIG_HOME
|
|
7
12
|
? path.join(process.env.XDG_CONFIG_HOME, 'opencode')
|
|
@@ -15,7 +20,7 @@ function loadConfig() {
|
|
|
15
20
|
const configContent = fs.readFileSync(configPath, 'utf-8');
|
|
16
21
|
const userConfig = JSON.parse(configContent);
|
|
17
22
|
return {
|
|
18
|
-
enabled: userConfig.enabled !== undefined ? userConfig.enabled : DEFAULT_CONFIG.enabled
|
|
23
|
+
enabled: userConfig.enabled !== undefined ? userConfig.enabled : DEFAULT_CONFIG.enabled,
|
|
19
24
|
};
|
|
20
25
|
}
|
|
21
26
|
catch (error) {
|
|
@@ -43,7 +48,7 @@ function renderStatusReport(status, config) {
|
|
|
43
48
|
'',
|
|
44
49
|
'Peak Hours (UTC+8):',
|
|
45
50
|
`- start: 14:00`,
|
|
46
|
-
`- end: 18:00
|
|
51
|
+
`- end: 18:00`,
|
|
47
52
|
];
|
|
48
53
|
return lines.join('\n');
|
|
49
54
|
}
|
|
@@ -56,70 +61,66 @@ export const PeakHours = async ({ client }) => {
|
|
|
56
61
|
path: { id: sessionID },
|
|
57
62
|
body: {
|
|
58
63
|
noReply: true,
|
|
59
|
-
parts: [{ type: 'text', text: output, ignored: true }]
|
|
60
|
-
}
|
|
64
|
+
parts: [{ type: 'text', text: output, ignored: true }],
|
|
65
|
+
},
|
|
61
66
|
});
|
|
62
67
|
}
|
|
63
68
|
catch (err) {
|
|
64
69
|
console.error('Failed to inject output:', err);
|
|
65
70
|
}
|
|
66
71
|
}
|
|
67
|
-
async function
|
|
72
|
+
async function injectCommandOutputAndHandle(sessionID, output) {
|
|
73
|
+
if (output !== undefined && output !== null) {
|
|
74
|
+
await injectRawOutput(sessionID, output);
|
|
75
|
+
}
|
|
76
|
+
handled();
|
|
77
|
+
}
|
|
78
|
+
async function handlePeakHoursSlashCommand(input) {
|
|
79
|
+
if (!config.enabled) {
|
|
80
|
+
return await injectCommandOutputAndHandle(input.sessionID, 'Peak Hours plugin is disabled');
|
|
81
|
+
}
|
|
68
82
|
const status = getPeakHoursStatus();
|
|
69
83
|
const output = formatPeakHoursMessage(status);
|
|
70
|
-
await
|
|
71
|
-
handled();
|
|
84
|
+
return await injectCommandOutputAndHandle(input.sessionID, output);
|
|
72
85
|
}
|
|
73
|
-
async function
|
|
86
|
+
async function handlePeakHoursStatusSlashCommand(input) {
|
|
87
|
+
if (!config.enabled) {
|
|
88
|
+
return await injectCommandOutputAndHandle(input.sessionID, 'Peak Hours plugin is disabled');
|
|
89
|
+
}
|
|
74
90
|
const status = getPeakHoursStatus();
|
|
75
91
|
const output = renderStatusReport(status, config);
|
|
76
|
-
await
|
|
77
|
-
handled();
|
|
92
|
+
return await injectCommandOutputAndHandle(input.sessionID, output);
|
|
78
93
|
}
|
|
79
94
|
return {
|
|
95
|
+
// Register built-in slash commands
|
|
80
96
|
config: async (input) => {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
97
|
+
const cfg = input;
|
|
98
|
+
cfg.command ??= {};
|
|
99
|
+
cfg.command['peak_hours'] = {
|
|
100
|
+
template: '/peak_hours',
|
|
101
|
+
description: 'Display current z.ai peak hours status and time until next transition',
|
|
102
|
+
};
|
|
103
|
+
cfg.command['peak_hours_status'] = {
|
|
104
|
+
template: '/peak_hours_status',
|
|
105
|
+
description: 'Display peak hours plugin diagnostics and configuration status',
|
|
90
106
|
};
|
|
91
107
|
},
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const sessionID = input.sessionID;
|
|
95
|
-
if (!config.enabled) {
|
|
96
|
-
await injectRawOutput(sessionID, 'Peak Hours plugin is disabled');
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
if (command === 'peak_hours') {
|
|
100
|
-
await handlePeakHoursCommand(sessionID);
|
|
101
|
-
}
|
|
102
|
-
else if (command === 'peak_hours_status') {
|
|
103
|
-
await handlePeakHoursStatusCommand(sessionID);
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
'session.created': async (input) => {
|
|
107
|
-
if (!config.enabled) {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
const status = getPeakHoursStatus();
|
|
111
|
-
const message = formatPeakHoursMessage(status);
|
|
108
|
+
// Intercept slash commands and handle them without LLM invocation
|
|
109
|
+
'command.execute.before': async (input) => {
|
|
112
110
|
try {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
111
|
+
const cmd = input.command;
|
|
112
|
+
if (cmd === 'peak_hours') {
|
|
113
|
+
return await handlePeakHoursSlashCommand(input);
|
|
114
|
+
}
|
|
115
|
+
if (cmd === 'peak_hours_status') {
|
|
116
|
+
return await handlePeakHoursStatusSlashCommand(input);
|
|
117
|
+
}
|
|
119
118
|
}
|
|
120
119
|
catch (err) {
|
|
121
|
-
|
|
120
|
+
// IMPORTANT: do not swallow command-handled sentinel errors.
|
|
121
|
+
// If this hook resolves, the command proceeds to the LLM.
|
|
122
|
+
throw err;
|
|
122
123
|
}
|
|
123
|
-
}
|
|
124
|
+
},
|
|
124
125
|
};
|
|
125
126
|
};
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|