@icaruk/zai-peak-hours 0.1.0 → 0.1.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 CHANGED
@@ -1,6 +1,14 @@
1
1
  # @icaruk/zai-peak-hours
2
2
 
3
- OpenCode plugin that displays z.ai peak hours information with automatic timezone detection via toast notifications.
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
+ - 🔔 Toast notification on session start
11
+ - 💬 Zero-token commands (`/peak_hours`, `/peak_hours_status`) - no LLM invocation
4
12
 
5
13
  ## Installation
6
14
 
@@ -12,23 +20,20 @@ Add to your `~/.config/opencode/opencode.json`:
12
20
  }
13
21
  ```
14
22
 
15
- ## Features
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
23
+ Restart OpenCode to load the plugin.
23
24
 
24
25
  ## Commands
25
26
 
27
+ Commands are automatically available after plugin installation:
28
+
26
29
  - `/peak_hours` - Display current peak hours status
27
- - `/peak_hours_status` - Display plugin diagnostics and configuration
30
+ - `/peak_hours_status` - Display plugin diagnostics and configuration status
31
+
32
+ These commands execute locally without invoking LLM (zero token usage) via `command.execute.before` hook.
28
33
 
29
34
  ## Messages
30
35
 
31
- The plugin displays toast notifications:
36
+ The plugin displays a toast notification when a new session is created:
32
37
 
33
38
  **Peak Hours:**
34
39
  ```
@@ -40,6 +45,16 @@ Currently in peak hours. X hours Y minutes remaining
40
45
  Currently in off-peak hours. X hours Y minutes remaining
41
46
  ```
42
47
 
48
+ ## Configuration
49
+
50
+ Optional configuration file at `~/.config/opencode/peak-hours.json`:
51
+
52
+ ```json
53
+ {
54
+ "enabled": true
55
+ }
56
+ ```
57
+
43
58
  ## Development
44
59
 
45
60
  ```bash
@@ -57,7 +72,7 @@ npm run build
57
72
  npm run dev
58
73
  ```
59
74
 
60
- Add local config in `opencode.json`
75
+ Add local config in `opencode.json`:
61
76
 
62
77
  ```json
63
78
  {
@@ -0,0 +1,2 @@
1
+ export declare const COMMAND_HANDLED_SENTINEL: "__PEAK_HOURS_COMMAND_HANDLED__";
2
+ export declare function handled(): never;
@@ -0,0 +1,4 @@
1
+ export const COMMAND_HANDLED_SENTINEL = "__PEAK_HOURS_COMMAND_HANDLED__";
2
+ export function handled() {
3
+ throw new Error(COMMAND_HANDLED_SENTINEL);
4
+ }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import type { Plugin } from '@opencode-ai/plugin';
2
- export declare const PeakHours: Plugin;
1
+ export { PeakHoursPlugin } from "./plugin.js";
2
+ export type { PeakHoursStatus, } from "./peak-hours.js";
package/dist/index.js CHANGED
@@ -1,125 +1 @@
1
- import { getPeakHoursStatus, formatPeakHoursMessage } from './peak-hours';
2
- import { DEFAULT_CONFIG } from './config';
3
- import * as fs from 'node:fs';
4
- import * as path from 'node:path';
5
- function getConfigPath() {
6
- const configDir = process.env.XDG_CONFIG_HOME
7
- ? path.join(process.env.XDG_CONFIG_HOME, 'opencode')
8
- : path.join(process.env.HOME || process.env.USERPROFILE || '', '.config', 'opencode');
9
- return path.join(configDir, 'peak-hours.json');
10
- }
11
- function loadConfig() {
12
- const configPath = getConfigPath();
13
- if (fs.existsSync(configPath)) {
14
- try {
15
- const configContent = fs.readFileSync(configPath, 'utf-8');
16
- const userConfig = JSON.parse(configContent);
17
- return {
18
- enabled: userConfig.enabled !== undefined ? userConfig.enabled : DEFAULT_CONFIG.enabled
19
- };
20
- }
21
- catch (error) {
22
- console.error('Error loading peak-hours config:', error);
23
- }
24
- }
25
- return DEFAULT_CONFIG;
26
- }
27
- function renderCommandHeading(title) {
28
- return `${title}\n${'='.repeat(title.length)}`;
29
- }
30
- function renderStatusReport(status, config) {
31
- const currentTime = new Date().toISOString();
32
- const lines = [
33
- renderCommandHeading('Peak Hours Status'),
34
- '',
35
- 'Configuration:',
36
- `- enabled: ${config.enabled}`,
37
- '',
38
- 'Current Status:',
39
- `- current_time: ${currentTime}`,
40
- `- in_peak_hours: ${status.inPeakHours}`,
41
- `- transition: ${status.transitionType}`,
42
- `- time_until_${status.transitionType}: ${status.timeUntilTransition}`,
43
- '',
44
- 'Peak Hours (UTC+8):',
45
- `- start: 14:00`,
46
- `- end: 18:00`
47
- ];
48
- return lines.join('\n');
49
- }
50
- export const PeakHours = async ({ client }) => {
51
- const config = loadConfig();
52
- const typedClient = client;
53
- async function injectRawOutput(sessionID, output) {
54
- try {
55
- await typedClient.session.prompt({
56
- path: { id: sessionID },
57
- body: {
58
- noReply: true,
59
- parts: [{ type: 'text', text: output, ignored: true }]
60
- }
61
- });
62
- }
63
- catch (err) {
64
- console.error('Failed to inject output:', err);
65
- }
66
- }
67
- async function handlePeakHoursCommand(sessionID) {
68
- const status = getPeakHoursStatus();
69
- const output = formatPeakHoursMessage(status);
70
- await injectRawOutput(sessionID, output);
71
- handled();
72
- }
73
- async function handlePeakHoursStatusCommand(sessionID) {
74
- const status = getPeakHoursStatus();
75
- const output = renderStatusReport(status, config);
76
- await injectRawOutput(sessionID, output);
77
- handled();
78
- }
79
- return {
80
- config: async (input) => {
81
- input.output.command = {
82
- peak_hours: {
83
- template: '',
84
- description: 'Display current z.ai peak hours status and time until next transition'
85
- },
86
- peak_hours_status: {
87
- template: '',
88
- description: 'Display peak hours plugin diagnostics and configuration status'
89
- }
90
- };
91
- },
92
- 'tui.command.execute': async (input) => {
93
- const command = input.command;
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);
112
- try {
113
- await typedClient.tui.showToast({
114
- body: {
115
- message,
116
- variant: status.inPeakHours ? 'warning' : 'info'
117
- }
118
- });
119
- }
120
- catch (err) {
121
- console.error('Failed to show toast:', err);
122
- }
123
- }
124
- };
125
- };
1
+ export { PeakHoursPlugin } from "./plugin.js";
@@ -0,0 +1,2 @@
1
+ import type { Plugin } from '@opencode-ai/plugin';
2
+ export declare const PeakHoursPlugin: Plugin;
package/dist/plugin.js ADDED
@@ -0,0 +1,90 @@
1
+ import { getPeakHoursStatus, formatPeakHoursMessage } from './peak-hours';
2
+ import { DEFAULT_CONFIG } from './config';
3
+ import * as fs from 'node:fs';
4
+ import * as path from 'node:path';
5
+ function getConfigPath() {
6
+ const configDir = process.env.XDG_CONFIG_HOME
7
+ ? path.join(process.env.XDG_CONFIG_HOME, 'opencode')
8
+ : path.join(process.env.HOME || process.env.USERPROFILE || '', '.config', 'opencode');
9
+ return path.join(configDir, 'peak-hours.json');
10
+ }
11
+ function loadConfig() {
12
+ const configPath = getConfigPath();
13
+ if (fs.existsSync(configPath)) {
14
+ try {
15
+ const configContent = fs.readFileSync(configPath, 'utf-8');
16
+ const userConfig = JSON.parse(configContent);
17
+ return {
18
+ enabled: userConfig.enabled !== undefined ? userConfig.enabled : DEFAULT_CONFIG.enabled
19
+ };
20
+ }
21
+ catch (error) {
22
+ console.error('Error loading peak-hours config:', error);
23
+ }
24
+ }
25
+ return DEFAULT_CONFIG;
26
+ }
27
+ function renderCommandHeading(title) {
28
+ return `${title}\n${'='.repeat(title.length)}`;
29
+ }
30
+ function renderStatusReport(status, config) {
31
+ const currentTime = new Date().toISOString();
32
+ const lines = [
33
+ renderCommandHeading('Peak Hours Status'),
34
+ '',
35
+ 'Configuration:',
36
+ `- enabled: ${config.enabled}`,
37
+ '',
38
+ 'Current Status:',
39
+ `- current_time: ${currentTime}`,
40
+ `- in_peak_hours: ${status.inPeakHours}`,
41
+ `- transition: ${status.transitionType}`,
42
+ `- time_until_${status.transitionType}: ${status.timeUntilTransition}`,
43
+ '',
44
+ 'Peak Hours (UTC+8):',
45
+ `- start: 14:00`,
46
+ `- end: 18:00`
47
+ ];
48
+ return lines.join('\n');
49
+ }
50
+ export const PeakHoursPlugin = async ({ client }) => {
51
+ const config = loadConfig();
52
+ const typedClient = client;
53
+ return {
54
+ 'command.execute.before': async (input, output) => {
55
+ const command = input.command;
56
+ if (!config.enabled) {
57
+ output.parts = [{ type: 'text', text: 'Peak Hours plugin is disabled', ignored: true }];
58
+ return;
59
+ }
60
+ if (command === 'peak_hours') {
61
+ const status = getPeakHoursStatus();
62
+ const message = formatPeakHoursMessage(status);
63
+ output.parts = [{ type: 'text', text: message, ignored: true }];
64
+ }
65
+ else if (command === 'peak_hours_status') {
66
+ const status = getPeakHoursStatus();
67
+ const report = renderStatusReport(status, config);
68
+ output.parts = [{ type: 'text', text: report, ignored: true }];
69
+ }
70
+ },
71
+ 'session.created': async (_input) => {
72
+ if (!config.enabled) {
73
+ return;
74
+ }
75
+ const status = getPeakHoursStatus();
76
+ const message = formatPeakHoursMessage(status);
77
+ try {
78
+ await typedClient.tui.showToast({
79
+ body: {
80
+ message,
81
+ variant: status.inPeakHours ? 'warning' : 'info'
82
+ }
83
+ });
84
+ }
85
+ catch (err) {
86
+ console.error('Failed to show toast:', err);
87
+ }
88
+ }
89
+ };
90
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icaruk/zai-peak-hours",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "OpenCode plugin to display z.ai peak hours information with automatic timezone detection",
6
6
  "main": "dist/index.js",