@jetcrab/pi-desk-sdk 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/LICENSE +201 -0
- package/README.md +4 -2
- package/dist/browser.d.ts +388 -0
- package/dist/browser.js +66 -0
- package/dist/capabilities.d.ts +104 -0
- package/dist/capabilities.js +89 -0
- package/dist/entry.d.ts +137 -0
- package/dist/entry.js +11 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +14 -0
- package/dist/host-runtime/base.js +284 -0
- package/dist/host-runtime/markdown.js +130 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/react-base.d.ts +33 -0
- package/dist/react-base.js +120 -0
- package/dist/react-log.d.ts +12 -0
- package/dist/react-log.js +99 -0
- package/dist/react-markdown.d.ts +5 -0
- package/dist/react-markdown.js +16 -0
- package/dist/react-ui.d.ts +197 -0
- package/dist/react-ui.js +310 -0
- package/dist/react.d.ts +2 -0
- package/dist/react.js +2 -0
- package/dist/session.d.ts +155 -0
- package/dist/session.js +167 -0
- package/dist/settings.d.ts +24 -0
- package/dist/settings.js +22 -0
- package/dist/shared.d.ts +31 -0
- package/dist/shared.js +11 -0
- package/package.json +118 -3
package/dist/session.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { normalizePluginIdentifier } from "./shared.js";
|
|
3
|
+
export { PluginMethodError, isPluginMethodError } from "./errors.js";
|
|
4
|
+
export const SESSION_PLUGIN_STATE_EVENT = 'pi-desk:session-plugins';
|
|
5
|
+
export const SESSION_PLUGIN_METHOD_EVENT = 'pi-desk:session-plugin-methods:v1';
|
|
6
|
+
export const SESSION_PLUGIN_PUSH_EVENT = 'pi-desk:session-plugin-push:v1';
|
|
7
|
+
export const SESSION_PLUGIN_CAPABILITIES_EVENT = 'pi-desk:session-capabilities';
|
|
8
|
+
export function bindSessionPlugin(pi, pluginName) {
|
|
9
|
+
const name = normalizePluginIdentifier(pluginName, 'pluginName');
|
|
10
|
+
return {
|
|
11
|
+
name,
|
|
12
|
+
setState(state) {
|
|
13
|
+
pi.events.emit(SESSION_PLUGIN_STATE_EVENT, { [name]: state });
|
|
14
|
+
},
|
|
15
|
+
push(eventInput, data) {
|
|
16
|
+
const event = normalizePluginIdentifier(eventInput, 'event');
|
|
17
|
+
pi.events.emit(SESSION_PLUGIN_PUSH_EVENT, {
|
|
18
|
+
pluginName: name,
|
|
19
|
+
event,
|
|
20
|
+
data
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
registerMethod(methodInput, execute) {
|
|
24
|
+
const method = normalizePluginIdentifier(methodInput, 'method');
|
|
25
|
+
const registration = {
|
|
26
|
+
pluginName: name,
|
|
27
|
+
method,
|
|
28
|
+
execute
|
|
29
|
+
};
|
|
30
|
+
pi.events.emit(SESSION_PLUGIN_METHOD_EVENT, { type: 'register', registration });
|
|
31
|
+
let registered = true;
|
|
32
|
+
return () => {
|
|
33
|
+
if (!registered)
|
|
34
|
+
return;
|
|
35
|
+
registered = false;
|
|
36
|
+
pi.events.emit(SESSION_PLUGIN_METHOD_EVENT, { type: 'unregister', registration });
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
onCapabilitiesChange(execute) {
|
|
40
|
+
const registration = {
|
|
41
|
+
pluginName: name,
|
|
42
|
+
execute,
|
|
43
|
+
hostManaged: false
|
|
44
|
+
};
|
|
45
|
+
let active = true;
|
|
46
|
+
pi.events.emit(SESSION_PLUGIN_CAPABILITIES_EVENT, { type: 'register', registration });
|
|
47
|
+
const unregister = () => {
|
|
48
|
+
if (!active)
|
|
49
|
+
return;
|
|
50
|
+
active = false;
|
|
51
|
+
pi.events.emit(SESSION_PLUGIN_CAPABILITIES_EVENT, { type: 'unregister', registration });
|
|
52
|
+
};
|
|
53
|
+
pi.on('session_start', async (_event, context) => {
|
|
54
|
+
if (active && !registration.hostManaged) {
|
|
55
|
+
await execute({ tools: {}, skills: {}, capabilities: {} }, context);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
pi.on('session_shutdown', unregister);
|
|
59
|
+
return unregister;
|
|
60
|
+
},
|
|
61
|
+
startTask(input) {
|
|
62
|
+
return startTask(pi, input);
|
|
63
|
+
},
|
|
64
|
+
reportTasks(tasks) {
|
|
65
|
+
reportTasks(pi, tasks);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export const TASK_REPORT_EVENT = 'pi-desk:task-report:v1';
|
|
70
|
+
function timestamp() {
|
|
71
|
+
return Date.now();
|
|
72
|
+
}
|
|
73
|
+
function terminalTask(snapshot, status, activity) {
|
|
74
|
+
const source = snapshot.detailSource;
|
|
75
|
+
const session = source?.kind === 'pi-conversation' ? source.session : undefined;
|
|
76
|
+
const detailSource = session?.sessionFile
|
|
77
|
+
? { kind: 'pi-conversation', sessionFile: session.sessionFile }
|
|
78
|
+
: source;
|
|
79
|
+
return {
|
|
80
|
+
...snapshot,
|
|
81
|
+
status,
|
|
82
|
+
activity: activity === undefined ? snapshot.activity : activity,
|
|
83
|
+
endedAt: timestamp(),
|
|
84
|
+
detailSource,
|
|
85
|
+
interrupt: null
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function emit(target, event) {
|
|
89
|
+
target.events.emit(TASK_REPORT_EVENT, event);
|
|
90
|
+
}
|
|
91
|
+
export function reportTasks(target, tasks) {
|
|
92
|
+
emit(target, { type: 'upsert', tasks });
|
|
93
|
+
}
|
|
94
|
+
export function startTask(target, input) {
|
|
95
|
+
let snapshot = {
|
|
96
|
+
taskId: input.taskId ?? randomUUID(),
|
|
97
|
+
taskKind: input.taskKind ?? null,
|
|
98
|
+
taskType: input.taskType ?? null,
|
|
99
|
+
title: input.title,
|
|
100
|
+
info: input.info?.map((item) => ({ ...item })) ?? [],
|
|
101
|
+
...(input.infoLoader ? { infoLoader: input.infoLoader } : {}),
|
|
102
|
+
status: 'running',
|
|
103
|
+
activity: input.activity ?? null,
|
|
104
|
+
startedAt: input.startedAt ?? timestamp(),
|
|
105
|
+
endedAt: null,
|
|
106
|
+
detailSource: input.detailSource ?? null,
|
|
107
|
+
interrupt: input.interrupt ?? null
|
|
108
|
+
};
|
|
109
|
+
let removed = false;
|
|
110
|
+
const report = () => {
|
|
111
|
+
if (removed)
|
|
112
|
+
throw new Error('Task has been removed');
|
|
113
|
+
emit(target, { type: 'upsert', tasks: [snapshot] });
|
|
114
|
+
};
|
|
115
|
+
report();
|
|
116
|
+
return {
|
|
117
|
+
get taskId() {
|
|
118
|
+
return snapshot.taskId;
|
|
119
|
+
},
|
|
120
|
+
update(input) {
|
|
121
|
+
if (snapshot.status !== 'running')
|
|
122
|
+
throw new Error('Terminal task cannot be updated');
|
|
123
|
+
snapshot = {
|
|
124
|
+
...snapshot,
|
|
125
|
+
...input,
|
|
126
|
+
taskKind: input.taskKind === undefined ? snapshot.taskKind : input.taskKind,
|
|
127
|
+
taskType: input.taskType === undefined ? snapshot.taskType : input.taskType,
|
|
128
|
+
info: input.info === undefined ? snapshot.info : input.info.map((item) => ({ ...item })),
|
|
129
|
+
infoLoader: input.infoLoader === undefined ? snapshot.infoLoader : input.infoLoader,
|
|
130
|
+
activity: input.activity === undefined ? snapshot.activity : input.activity,
|
|
131
|
+
detailSource: input.detailSource === undefined ? snapshot.detailSource : input.detailSource,
|
|
132
|
+
interrupt: input.interrupt === undefined ? snapshot.interrupt : input.interrupt
|
|
133
|
+
};
|
|
134
|
+
report();
|
|
135
|
+
},
|
|
136
|
+
complete(activity) {
|
|
137
|
+
if (snapshot.status !== 'running')
|
|
138
|
+
throw new Error('Task is already terminal');
|
|
139
|
+
snapshot = terminalTask(snapshot, 'completed', activity);
|
|
140
|
+
report();
|
|
141
|
+
},
|
|
142
|
+
fail(activity) {
|
|
143
|
+
if (snapshot.status !== 'running')
|
|
144
|
+
throw new Error('Task is already terminal');
|
|
145
|
+
snapshot = terminalTask(snapshot, 'failed', activity);
|
|
146
|
+
report();
|
|
147
|
+
},
|
|
148
|
+
stopped(activity) {
|
|
149
|
+
if (snapshot.status !== 'running')
|
|
150
|
+
throw new Error('Task is already terminal');
|
|
151
|
+
snapshot = terminalTask(snapshot, 'stopped', activity);
|
|
152
|
+
report();
|
|
153
|
+
},
|
|
154
|
+
interrupted(activity) {
|
|
155
|
+
if (snapshot.status !== 'running')
|
|
156
|
+
throw new Error('Task is already terminal');
|
|
157
|
+
snapshot = terminalTask(snapshot, 'interrupted', activity);
|
|
158
|
+
report();
|
|
159
|
+
},
|
|
160
|
+
remove() {
|
|
161
|
+
if (removed)
|
|
162
|
+
return;
|
|
163
|
+
removed = true;
|
|
164
|
+
emit(target, { type: 'remove', taskIds: [snapshot.taskId] });
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { PluginDisposer } from './shared.js';
|
|
3
|
+
export declare const HostRegionSchema: z.ZodReadonly<z.ZodObject<{
|
|
4
|
+
locale: z.ZodEnum<{
|
|
5
|
+
"zh-CN": "zh-CN";
|
|
6
|
+
en: "en";
|
|
7
|
+
}>;
|
|
8
|
+
timeZone: z.ZodString;
|
|
9
|
+
}, z.core.$strict>>;
|
|
10
|
+
export declare const HostSettingsSchema: z.ZodReadonly<z.ZodObject<{
|
|
11
|
+
region: z.ZodReadonly<z.ZodObject<{
|
|
12
|
+
locale: z.ZodEnum<{
|
|
13
|
+
"zh-CN": "zh-CN";
|
|
14
|
+
en: "en";
|
|
15
|
+
}>;
|
|
16
|
+
timeZone: z.ZodString;
|
|
17
|
+
}, z.core.$strict>>;
|
|
18
|
+
}, z.core.$strict>>;
|
|
19
|
+
export type HostRegion = z.infer<typeof HostRegionSchema>;
|
|
20
|
+
export type HostSettingsSnapshot = z.infer<typeof HostSettingsSchema>;
|
|
21
|
+
export interface HostSettings {
|
|
22
|
+
getSnapshot(): HostSettingsSnapshot;
|
|
23
|
+
subscribe(listener: () => void): PluginDisposer;
|
|
24
|
+
}
|
package/dist/settings.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const HostRegionSchema = z
|
|
3
|
+
.object({
|
|
4
|
+
locale: z.enum(['zh-CN', 'en']),
|
|
5
|
+
timeZone: z
|
|
6
|
+
.string()
|
|
7
|
+
.trim()
|
|
8
|
+
.min(1)
|
|
9
|
+
.max(100)
|
|
10
|
+
.refine((value) => {
|
|
11
|
+
try {
|
|
12
|
+
new Intl.DateTimeFormat('en', { timeZone: value });
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}, '无效的 IANA 时区')
|
|
19
|
+
})
|
|
20
|
+
.strict()
|
|
21
|
+
.readonly();
|
|
22
|
+
export const HostSettingsSchema = z.object({ region: HostRegionSchema }).strict().readonly();
|
package/dist/shared.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type PluginJsonValue = null | boolean | number | string | PluginJsonValue[] | {
|
|
2
|
+
[key: string]: PluginJsonValue;
|
|
3
|
+
};
|
|
4
|
+
export type PluginJsonObject = {
|
|
5
|
+
[key: string]: PluginJsonValue;
|
|
6
|
+
};
|
|
7
|
+
export type PluginDisposer = () => void | Promise<void>;
|
|
8
|
+
export type PluginNotificationLevel = 'info' | 'success' | 'warning' | 'error';
|
|
9
|
+
export interface PluginSource {
|
|
10
|
+
readonly workId: string;
|
|
11
|
+
readonly sessionId: string;
|
|
12
|
+
readonly branchId: string;
|
|
13
|
+
}
|
|
14
|
+
export interface PluginWorkSession {
|
|
15
|
+
readonly source: PluginSource;
|
|
16
|
+
readonly cwd: string;
|
|
17
|
+
readonly status: 'main_running' | 'background_running' | 'completed' | 'idle';
|
|
18
|
+
}
|
|
19
|
+
export interface BrowserApplicationContext {
|
|
20
|
+
source: PluginSource;
|
|
21
|
+
cwd: string;
|
|
22
|
+
}
|
|
23
|
+
export type BrowserBuiltinIconName = 'plugin' | 'server' | 'rocket' | 'settings' | 'panel' | 'message' | 'terminal' | 'file' | 'folder' | 'bot' | 'wrench' | 'database' | 'globe';
|
|
24
|
+
export type BrowserContributionIcon = {
|
|
25
|
+
type: 'builtin';
|
|
26
|
+
name: BrowserBuiltinIconName;
|
|
27
|
+
} | {
|
|
28
|
+
type: 'svg';
|
|
29
|
+
content: string;
|
|
30
|
+
};
|
|
31
|
+
export declare function normalizePluginIdentifier(value: string, field: 'pluginName' | 'method' | 'contributionName' | 'declarationName' | 'event'): string;
|
package/dist/shared.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const PLUGIN_IDENTIFIER_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
2
|
+
const PLUGIN_IDENTIFIER_MAX_LENGTH = 64;
|
|
3
|
+
export function normalizePluginIdentifier(value, field) {
|
|
4
|
+
const normalized = value.trim();
|
|
5
|
+
if (normalized.length === 0 ||
|
|
6
|
+
normalized.length > PLUGIN_IDENTIFIER_MAX_LENGTH ||
|
|
7
|
+
!PLUGIN_IDENTIFIER_PATTERN.test(normalized)) {
|
|
8
|
+
throw new Error(`${field} must use lowercase kebab-case and contain 1-64 characters`);
|
|
9
|
+
}
|
|
10
|
+
return normalized;
|
|
11
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,121 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jetcrab/pi-desk-sdk",
|
|
3
|
-
"
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
"repository": {
|
|
4
|
+
"type": "git",
|
|
5
|
+
"url": "git+https://github.com/JetCrab/pi-desk.git",
|
|
6
|
+
"directory": "plugins/pi-desk-sdk"
|
|
7
|
+
},
|
|
8
|
+
"version": "1.0.0",
|
|
9
|
+
"license": "Apache-2.0",
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"registry": "https://registry.npmjs.org",
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./settings": {
|
|
22
|
+
"types": "./dist/settings.d.ts",
|
|
23
|
+
"import": "./dist/settings.js",
|
|
24
|
+
"default": "./dist/settings.js"
|
|
25
|
+
},
|
|
26
|
+
"./errors": {
|
|
27
|
+
"types": "./dist/errors.d.ts",
|
|
28
|
+
"import": "./dist/errors.js",
|
|
29
|
+
"default": "./dist/errors.js"
|
|
30
|
+
},
|
|
31
|
+
"./capabilities": {
|
|
32
|
+
"types": "./dist/capabilities.d.ts",
|
|
33
|
+
"import": "./dist/capabilities.js",
|
|
34
|
+
"default": "./dist/capabilities.js"
|
|
35
|
+
},
|
|
36
|
+
"./entry": {
|
|
37
|
+
"types": "./dist/entry.d.ts",
|
|
38
|
+
"import": "./dist/entry.js",
|
|
39
|
+
"default": "./dist/entry.js"
|
|
40
|
+
},
|
|
41
|
+
"./session": {
|
|
42
|
+
"types": "./dist/session.d.ts",
|
|
43
|
+
"import": "./dist/session.js",
|
|
44
|
+
"default": "./dist/session.js"
|
|
45
|
+
},
|
|
46
|
+
"./browser": {
|
|
47
|
+
"types": "./dist/browser.d.ts",
|
|
48
|
+
"import": "./dist/browser.js",
|
|
49
|
+
"default": "./dist/browser.js"
|
|
50
|
+
},
|
|
51
|
+
"./react": {
|
|
52
|
+
"types": "./dist/react.d.ts",
|
|
53
|
+
"import": "./dist/react.js",
|
|
54
|
+
"default": "./dist/react.js"
|
|
55
|
+
},
|
|
56
|
+
"./react/base": {
|
|
57
|
+
"types": "./dist/react-base.d.ts",
|
|
58
|
+
"import": "./dist/react-base.js",
|
|
59
|
+
"default": "./dist/react-base.js"
|
|
60
|
+
},
|
|
61
|
+
"./react/markdown": {
|
|
62
|
+
"types": "./dist/react-markdown.d.ts",
|
|
63
|
+
"import": "./dist/react-markdown.js",
|
|
64
|
+
"default": "./dist/react-markdown.js"
|
|
65
|
+
},
|
|
66
|
+
"./host-runtime/*": "./dist/host-runtime/*"
|
|
67
|
+
},
|
|
68
|
+
"types": "./dist/index.d.ts",
|
|
69
|
+
"files": [
|
|
70
|
+
"dist",
|
|
71
|
+
"README.md"
|
|
72
|
+
],
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"zod": "^4.4.3"
|
|
75
|
+
},
|
|
76
|
+
"peerDependencies": {
|
|
77
|
+
"@base-ui/react": "^1.7.0",
|
|
78
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
79
|
+
"@streamdown/cjk": "^1.0.3",
|
|
80
|
+
"lucide-react": "^1.31.0",
|
|
81
|
+
"react": ">=19",
|
|
82
|
+
"react-dom": ">=19",
|
|
83
|
+
"streamdown": "^2.5.0"
|
|
84
|
+
},
|
|
85
|
+
"peerDependenciesMeta": {
|
|
86
|
+
"@base-ui/react": {
|
|
87
|
+
"optional": true
|
|
88
|
+
},
|
|
89
|
+
"@earendil-works/pi-coding-agent": {
|
|
90
|
+
"optional": true
|
|
91
|
+
},
|
|
92
|
+
"@streamdown/cjk": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
95
|
+
"lucide-react": {
|
|
96
|
+
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"react": {
|
|
99
|
+
"optional": true
|
|
100
|
+
},
|
|
101
|
+
"react-dom": {
|
|
102
|
+
"optional": true
|
|
103
|
+
},
|
|
104
|
+
"streamdown": {
|
|
105
|
+
"optional": true
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"devDependencies": {
|
|
109
|
+
"@base-ui/react": "^1.7.0",
|
|
110
|
+
"@streamdown/cjk": "^1.0.3",
|
|
111
|
+
"esbuild": "^0.28.2",
|
|
112
|
+
"lucide-react": "^1.31.0",
|
|
113
|
+
"react": "19.2.8",
|
|
114
|
+
"react-dom": "19.2.8",
|
|
115
|
+
"streamdown": "^2.5.0"
|
|
116
|
+
},
|
|
117
|
+
"scripts": {
|
|
118
|
+
"build": "tsc -p tsconfig.json && node scripts/build-host-runtime.mjs",
|
|
119
|
+
"test": "pnpm run build && node --test tests/*.test.mjs"
|
|
120
|
+
}
|
|
6
121
|
}
|