@xtalpi/agentic-lab-skills 0.0.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.
Files changed (57) hide show
  1. package/README.md +1 -0
  2. package/package.json +14 -0
  3. package/skills/lab-flow-designer/SKILL.md +360 -0
  4. package/skills/lab-flow-designer/embedded-template/SKILL.md +84 -0
  5. package/skills/lab-flow-designer/embedded-template/pools//345/205/245/345/217/243/346/261/240.md +12 -0
  6. package/skills/lab-flow-designer/embedded-template/pools//345/207/272/345/217/243/346/261/240.md +12 -0
  7. package/skills/lab-flow-designer/embedded-template/scripts//347/244/272/344/276/213/346/225/260/346/215/256/344/270/216/346/240/241/351/252/214/351/227/250/346/216/247.js +149 -0
  8. package/skills/lab-flow-designer/embedded-template/valves//347/244/272/344/276/213/346/225/260/346/215/256/344/270/216/346/240/241/351/252/214/351/227/250/346/216/247.md +100 -0
  9. package/skills/lab-flow-designer/references/agentic-lab-processer.md +101 -0
  10. package/skills/lab-flow-designer/references/agentic-lab-sdk.md +440 -0
  11. package/skills/lab-flow-designer/references/rhea-api/README.md +7 -0
  12. package/skills/lab-flow-designer/references/rhea-api/execute_process_batch.md +58 -0
  13. package/skills/lab-flow-designer/references/skill-package-layout.md +199 -0
  14. package/skills/lab-flow-designer/references//344/270/232/345/212/241/346/265/201/347/250/213/346/226/207/346/241/243/346/240/207/345/207/206.md +208 -0
  15. package/skills/lab-orbit-component-builder/SKILL.md +305 -0
  16. package/skills/lab-orbit-component-builder/examples/xnb-component-template/.env.local.example +27 -0
  17. package/skills/lab-orbit-component-builder/examples/xnb-component-template/.eslintignore +7 -0
  18. package/skills/lab-orbit-component-builder/examples/xnb-component-template/.eslintrc.cjs +88 -0
  19. package/skills/lab-orbit-component-builder/examples/xnb-component-template/.nvmrc +1 -0
  20. package/skills/lab-orbit-component-builder/examples/xnb-component-template/AgenticAppAPI.md +268 -0
  21. package/skills/lab-orbit-component-builder/examples/xnb-component-template/Jenkinsfile +106 -0
  22. package/skills/lab-orbit-component-builder/examples/xnb-component-template/OrbitAPI.md +453 -0
  23. package/skills/lab-orbit-component-builder/examples/xnb-component-template/README.md +176 -0
  24. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/public/index.html +12 -0
  25. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/App.vue +151 -0
  26. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/components/DevOpenerLauncher.vue +143 -0
  27. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/global.d.ts +74 -0
  28. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/main.ts +305 -0
  29. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/mockXNBBitable.ts +119 -0
  30. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/shims-vue.d.ts +6 -0
  31. package/skills/lab-orbit-component-builder/examples/xnb-component-template/dev/src/utils/devOpenerHost.ts +75 -0
  32. package/skills/lab-orbit-component-builder/examples/xnb-component-template/index.html +13 -0
  33. package/skills/lab-orbit-component-builder/examples/xnb-component-template/package.json +57 -0
  34. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/api/agenticlabTickets.ts +110 -0
  35. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/entries/bitable.ts +4 -0
  36. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/entries/custom-page.ts +4 -0
  37. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/index.ts +1 -0
  38. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/styles/orbit-quasar-host.scss +19 -0
  39. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/types/context.ts +15 -0
  40. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/types/xnb-context.ts +70 -0
  41. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useBitablePage.ts +180 -0
  42. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useSuperCellDemo.ts +257 -0
  43. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/use/useSuperTableBitableLifecycle.ts +547 -0
  44. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/openerInitParams.ts +158 -0
  45. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/openerTicketIds.ts +32 -0
  46. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/orbitHttpClient.ts +110 -0
  47. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/utils/request.ts +92 -0
  48. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/views/bitable.vue +65 -0
  49. package/skills/lab-orbit-component-builder/examples/xnb-component-template/src/views/custom-page.vue +140 -0
  50. package/skills/lab-orbit-component-builder/examples/xnb-component-template/tsconfig.json +48 -0
  51. package/skills/lab-orbit-component-builder/examples/xnb-component-template/vite.config.ts +165 -0
  52. package/skills/lab-orbit-component-builder/examples/xnb-component-template/vite.dev.config.ts +51 -0
  53. package/skills/lab-orbit-component-builder/references/flow-document-human-ui.md +65 -0
  54. package/skills/lab-orbit-component-builder/references/pool-schema-to-columns.md +67 -0
  55. package/skills/lab-orbit-component-builder/references/vue-template-checklist.md +113 -0
  56. package/skills/lab-orbit-component-builder/references/xnb-context-vue-props.md +49 -0
  57. package/skills/lab-orbit-component-builder/references/xnbitable-vue-parity.md +32 -0
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
7
+ <title>Xnb Component Dev</title>
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+ </body>
12
+ </html>
@@ -0,0 +1,151 @@
1
+ <template>
2
+ <div class="dev-shell" :class="{ 'dev-shell--child': isChildPage }">
3
+ <DevOpenerLauncher v-if="!isChildPage" class="dev-shell__launcher" />
4
+ <q-banner v-else rounded dense class="dev-child-hint q-ma-sm">
5
+ Opener 子页联调 · trace: <code>{{ childTrace }}</code> — 初始化结果见下方组件(成功则出现刷新/提交按钮)
6
+ </q-banner>
7
+ <div class="entry">
8
+ <View ref="sheetRef" :xnbContext="xnbContext" />
9
+ </div>
10
+ </div>
11
+ </template>
12
+
13
+ <script lang="ts">
14
+ import { computed, defineComponent, reactive, ref } from 'vue'
15
+ import { Notify, Loading } from 'quasar'
16
+ import { DevXNBBitable } from './mockXNBBitable'
17
+ import DevOpenerLauncher from './components/DevOpenerLauncher.vue'
18
+ import { isDevOpenerChildPage } from './utils/devOpenerHost'
19
+
20
+ export default defineComponent({
21
+ components: { DevOpenerLauncher },
22
+ setup () {
23
+ const isChildPage = isDevOpenerChildPage()
24
+ const childTrace = computed(() => new URLSearchParams(window.location.search).get('trace') || '—')
25
+ const cellParams = reactive({
26
+ bookPath: '/dev/notebook/doe',
27
+ uid: 'dev-cell-001'
28
+ })
29
+ const viewRef = ref()
30
+
31
+ const DEV_REQUEST_URL = import.meta.env.VITE_REQUEST_URL || ''
32
+
33
+ const userData: Record<string, any> = {
34
+ projectURL: JSON.stringify({ request_url: DEV_REQUEST_URL })
35
+ }
36
+
37
+ const xnbContext = {
38
+ bookPath: cellParams.bookPath,
39
+ cellUid: cellParams.uid,
40
+ XNBUtil: {
41
+ async getWikiTables (_url: string): Promise<any> {
42
+ return { bitable: 'dev-bitable', tables: [] }
43
+ },
44
+ async getLarkBitableById (_bitable: string, _tableId: string, _opts?: any): Promise<any[]> {
45
+ return []
46
+ },
47
+ async batchCreateRecords (_opts: any): Promise<any> {
48
+ return { records: [] }
49
+ }
50
+ },
51
+ notifySuccess (msg: string): void {
52
+ Notify.create({ type: 'positive', message: String(msg), position: 'top', timeout: 3000 })
53
+ },
54
+ notifyError (msg: any): void {
55
+ const text = msg instanceof Error ? msg.message : String(msg)
56
+ Notify.create({ type: 'negative', message: text, position: 'top', timeout: 5000 })
57
+ },
58
+ loadingShow (opts?: any): void {
59
+ Loading.show(typeof opts === 'string' ? { message: opts } : opts)
60
+ },
61
+ loadingHide (): void {
62
+ Loading.hide()
63
+ },
64
+ userData,
65
+ getUserData (): Record<string, any> {
66
+ return userData
67
+ },
68
+ updateUserData (data: any): void {
69
+ Object.assign(userData, data)
70
+ },
71
+ mergeUserData (data: any): void {
72
+ Object.assign(userData, data)
73
+ },
74
+ getInstanceById (_id: string): any { return null },
75
+ getInstanceValueById (_id: string): any { return null },
76
+ setInstanceValueById (_id: string, _value: any): void { /* noop */ },
77
+ async saveBitableConfig (_bitable: Record<string, any>): Promise<void> {
78
+ localStorage.setItem('DEV_BITABLE_CONFIG', JSON.stringify(_bitable))
79
+ },
80
+ async getBitableConfig (): Promise<string> {
81
+ return localStorage.getItem('DEV_BITABLE_CONFIG') || '{}'
82
+ },
83
+ XNBBitable: DevXNBBitable
84
+ }
85
+
86
+ window.xnb.store.set('params', cellParams)
87
+
88
+ return {
89
+ isChildPage,
90
+ childTrace,
91
+ cellParams,
92
+ viewRef,
93
+ xnbContext
94
+ }
95
+ }
96
+ })
97
+ </script>
98
+
99
+ <style lang="scss">
100
+ html,
101
+ body,
102
+ #app {
103
+ margin: 0;
104
+ padding: 0;
105
+ height: 100%;
106
+ width: 100%;
107
+ overflow: hidden;
108
+ box-sizing: border-box;
109
+ }
110
+
111
+ #app {
112
+ font-family: Arial, Helvetica, sans-serif;
113
+ -webkit-font-smoothing: antialiased;
114
+ -moz-osx-font-smoothing: grayscale;
115
+ }
116
+
117
+ .dev-shell {
118
+ display: flex;
119
+ flex-direction: column;
120
+ height: 100%;
121
+ width: 100%;
122
+ box-sizing: border-box;
123
+ &--child .entry {
124
+ flex: 1;
125
+ min-height: 0;
126
+ }
127
+ &__launcher {
128
+ flex-shrink: 0;
129
+ margin: 8px 8px 0;
130
+ }
131
+ }
132
+
133
+ .dev-child-hint {
134
+ flex-shrink: 0;
135
+ background: #fffbeb;
136
+ border: 1px solid #f0e4b8;
137
+ code {
138
+ font-size: 0.75rem;
139
+ }
140
+ }
141
+
142
+ .entry {
143
+ font-family: Arial, Helvetica, sans-serif;
144
+ box-sizing: border-box;
145
+ flex: 1;
146
+ min-height: 0;
147
+ width: 100%;
148
+ margin: 0;
149
+ overflow-y: auto;
150
+ }
151
+ </style>
@@ -0,0 +1,143 @@
1
+ <template>
2
+ <q-banner rounded dense class="dev-opener-launcher q-mb-sm">
3
+ <div class="text-subtitle2 q-mb-xs">Opener 联调(模拟宿主打开子页)</div>
4
+ <p class="text-caption text-grey-8 q-mb-sm">
5
+ 点击下方按钮在新窗口打开子页;子页通过 <code>?trace=</code> 与 <code>postMessage</code> 拉取下方 JSON。
6
+ 组件:<strong>{{ componentId }}</strong>
7
+ </p>
8
+ <q-input
9
+ v-model="payloadText"
10
+ type="textarea"
11
+ autogrow
12
+ dense
13
+ outlined
14
+ label="模拟 opener payload(JSON)"
15
+ :error="parseError != null"
16
+ :error-message="parseError || undefined"
17
+ class="q-mb-sm"
18
+ />
19
+ <div class="row items-center q-gutter-sm flex-wrap">
20
+ <q-btn
21
+ color="primary"
22
+ dense
23
+ unelevated
24
+ label="以 opener 方式打开子页"
25
+ :disable="parseError != null"
26
+ @click="onOpenChild"
27
+ />
28
+ <span v-if="lastTrace" class="text-caption text-grey-7">
29
+ 最近 trace: <code>{{ lastTrace }}</code>
30
+ <span v-if="childBlocked" class="text-negative">(弹窗被拦截,请允许本站点弹窗)</span>
31
+ </span>
32
+ </div>
33
+ </q-banner>
34
+ </template>
35
+
36
+ <script lang="ts">
37
+ import { defineComponent, onMounted, onUnmounted, ref, watch } from 'vue'
38
+ import { parseDevOpenerInitFromEnv } from '@/utils/openerInitParams'
39
+ import {
40
+ createDevOpenerTrace,
41
+ installDevOpenerHost,
42
+ openDevOpenerChild
43
+ } from '../utils/devOpenerHost'
44
+ import type { DevOpenerHostHandle } from '../utils/devOpenerHost'
45
+
46
+ const DEFAULT_PAYLOAD = {
47
+ ticket_ids: [450, 453],
48
+ valve_id: 0
49
+ }
50
+
51
+ function defaultPayloadText (): string {
52
+ const fromEnv = parseDevOpenerInitFromEnv()
53
+ if (fromEnv != null) {
54
+ try {
55
+ return JSON.stringify(fromEnv, null, 2)
56
+ } catch {
57
+ /* fall through */
58
+ }
59
+ }
60
+ return JSON.stringify(DEFAULT_PAYLOAD, null, 2)
61
+ }
62
+
63
+ function parsePayloadJson (text: string): { value: unknown | null; error: string | null } {
64
+ const trimmed = text.trim()
65
+ if (trimmed === '') return { value: null, error: 'JSON 不能为空' }
66
+ try {
67
+ return { value: JSON.parse(trimmed) as unknown, error: null }
68
+ } catch {
69
+ return { value: null, error: '不是合法 JSON' }
70
+ }
71
+ }
72
+
73
+ export default defineComponent({
74
+ name: 'DevOpenerLauncher',
75
+ setup () {
76
+ const componentId = import.meta.env.VITE_COMPONENT_ID || 'bitable'
77
+ const payloadText = ref(defaultPayloadText())
78
+ const parseError = ref<string | null>(null)
79
+ const lastTrace = ref<string | null>(null)
80
+ const childBlocked = ref(false)
81
+
82
+ let hostHandle: DevOpenerHostHandle | null = null
83
+
84
+ function syncParseError (): void {
85
+ parseError.value = parsePayloadJson(payloadText.value).error
86
+ }
87
+
88
+ function getPayload (): unknown {
89
+ const { value, error } = parsePayloadJson(payloadText.value)
90
+ if (error != null) throw new Error(error)
91
+ return value
92
+ }
93
+
94
+ function onOpenChild (): void {
95
+ syncParseError()
96
+ if (parseError.value != null) return
97
+
98
+ const trace = createDevOpenerTrace()
99
+ lastTrace.value = trace
100
+ childBlocked.value = false
101
+
102
+ const child = openDevOpenerChild(trace)
103
+ if (child == null) {
104
+ childBlocked.value = true
105
+ console.warn('[devOpenerHost] window.open 返回 null,可能被浏览器拦截')
106
+ }
107
+ }
108
+
109
+ watch(payloadText, () => {
110
+ syncParseError()
111
+ })
112
+
113
+ onMounted(() => {
114
+ syncParseError()
115
+ hostHandle = installDevOpenerHost(() => getPayload())
116
+ })
117
+
118
+ onUnmounted(() => {
119
+ hostHandle?.dispose()
120
+ hostHandle = null
121
+ })
122
+
123
+ return {
124
+ componentId,
125
+ payloadText,
126
+ parseError,
127
+ lastTrace,
128
+ childBlocked,
129
+ onOpenChild
130
+ }
131
+ }
132
+ })
133
+ </script>
134
+
135
+ <style scoped lang="scss">
136
+ .dev-opener-launcher {
137
+ background: #f0f7ff;
138
+ border: 1px solid #c5d9f0;
139
+ code {
140
+ font-size: 0.75rem;
141
+ }
142
+ }
143
+ </style>
@@ -0,0 +1,74 @@
1
+ declare module '@xtalpi/webb-api'
2
+ declare module 'axios'
3
+ declare module 'lodash'
4
+
5
+ interface XnbHttpClient {
6
+ request: (options: {
7
+ method: string;
8
+ url: string;
9
+ headers?: Record<string, string>;
10
+ responseType?: number;
11
+ query?: Record<string, string>;
12
+ body?: { type: string; data: any };
13
+ }) => Promise<{ data: any; status: number; ok: boolean; headers: Record<string, string> }>;
14
+ }
15
+
16
+ interface XnbHttp {
17
+ client: XnbHttpClient;
18
+ Body: {
19
+ json: (data: any) => { type: string; data: any };
20
+ text: (text: string) => { type: string; data: string };
21
+ form: (data: Record<string, any>) => { type: string; data: Record<string, any> };
22
+ };
23
+ ResponseType: {
24
+ JSON: number;
25
+ Text: number;
26
+ Binary: number;
27
+ };
28
+ }
29
+
30
+ interface XnbChoreo {
31
+ getUserToken: (bookPath?: string) => Promise<string>;
32
+ getUserInfo: (options?: any) => Promise<{ userName: string; email: string }>;
33
+ }
34
+
35
+ interface Xnb {
36
+ book: Record<string, (...args: any[]) => any>;
37
+ store: Record<string, (...args: any[]) => any>;
38
+ event: any;
39
+ kernel: Record<string, (...args: any[]) => any>;
40
+ choreo: XnbChoreo;
41
+ http: XnbHttp;
42
+ app: Record<string, (...args: any[]) => any>;
43
+ utils: Record<string, any>;
44
+ }
45
+
46
+ interface WebbApi {
47
+ register: (name: string, value: any) => void;
48
+ getUrl: () => { webbHost: string };
49
+ store: Record<string, (...args: any[]) => any>;
50
+ utils: Record<string, any>;
51
+ event?: { on: (...args: any[]) => void; emit: (...args: any[]) => void; off: (...args: any[]) => void };
52
+ ui: {
53
+ message: (opts: { type?: string; message: string }) => void;
54
+ };
55
+ method: {
56
+ call: (module: string, method: string, ...args: any[]) => any;
57
+ };
58
+ [key: string]: any;
59
+ }
60
+
61
+ interface QuasarGlobal {
62
+ loading: { show: (opts?: any) => void; hide: () => void };
63
+ notify: (opts: any) => void;
64
+ dialog: (opts: any) => any;
65
+ dark: any;
66
+ }
67
+
68
+ interface Window {
69
+ Vue: any;
70
+ vueInstance: any;
71
+ webb: WebbApi;
72
+ xnb: Xnb;
73
+ $q: QuasarGlobal;
74
+ }
@@ -0,0 +1,305 @@
1
+ import * as Vue from 'vue'
2
+ import dayjs from 'dayjs'
3
+
4
+ import {
5
+ Quasar,
6
+ Notify,
7
+ Loading,
8
+ Dialog,
9
+ Dark,
10
+ QBtn,
11
+ QIcon,
12
+ QBanner,
13
+ QSpace,
14
+ QSeparator,
15
+ QTable,
16
+ QTd,
17
+ QTr,
18
+ QTh,
19
+ QChip,
20
+ QInput,
21
+ QSelect,
22
+ QToggle,
23
+ QBadge,
24
+ QList,
25
+ QItem,
26
+ QItemSection,
27
+ QCard,
28
+ QCardSection,
29
+ QCardActions,
30
+ QDialog,
31
+ QLinearProgress,
32
+ QTooltip,
33
+ QCheckbox,
34
+ QSpinnerGears,
35
+ QSpinnerDots,
36
+ QPagination,
37
+ QExpansionItem,
38
+ ClosePopup,
39
+ Ripple
40
+ } from 'quasar'
41
+ import quasarLangEN from 'quasar/lang/en-US'
42
+ import '@quasar/extras/material-icons/material-icons.css'
43
+ import 'quasar/src/css/index.sass'
44
+
45
+ import { Webb } from '@xtalpi/webb-api'
46
+ import axios from 'axios'
47
+ import lodash from 'lodash'
48
+
49
+ import App from './App.vue'
50
+
51
+ // ==================== 多入口视图加载 ====================
52
+
53
+ const COMPONENT_ID = import.meta.env.VITE_COMPONENT_ID || 'bitable'
54
+ const viewModules = import.meta.glob('../../src/views/*.vue', { eager: true })
55
+ const viewKey = `../../src/views/${COMPONENT_ID}.vue`
56
+ const viewModule = viewModules[viewKey] as { default: any } | undefined
57
+ if (!viewModule) {
58
+ const available = Object.keys(viewModules).map((k: string) => k.replace('../../src/views/', '').replace('.vue', '')).join(', ')
59
+ throw new Error(
60
+ `[dev] VITE_COMPONENT_ID=${COMPONENT_ID} 无对应视图。当前已有: ${available}`
61
+ )
62
+ }
63
+ const View = viewModule.default
64
+
65
+ // ==================== Store ====================
66
+
67
+ const cached: Record<string, any> = {}
68
+ const store = {
69
+ getToken: (): string | null => localStorage.getItem('access_token'),
70
+ onLogout: (): void => {},
71
+ set: (key: string, value: any): void => {
72
+ cached[key] = value
73
+ },
74
+ get: (key: string): any => {
75
+ return cached[key]
76
+ },
77
+ getUser: (): any => ({
78
+ email: 'dev@local.test',
79
+ userName: 'DevUser'
80
+ })
81
+ }
82
+
83
+ const utils = {
84
+ lodash,
85
+ axios
86
+ }
87
+ Webb.register('store', store)
88
+ Webb.register('utils', utils)
89
+
90
+ const app = Vue.createApp(App)
91
+
92
+ app.use(Quasar, {
93
+ plugins: {
94
+ Notify,
95
+ Loading,
96
+ Dialog
97
+ },
98
+ components: {
99
+ QBtn,
100
+ QIcon,
101
+ QBanner,
102
+ QSpace,
103
+ QSeparator,
104
+ QTable,
105
+ QTd,
106
+ QTr,
107
+ QTh,
108
+ QChip,
109
+ QInput,
110
+ QSelect,
111
+ QToggle,
112
+ QBadge,
113
+ QList,
114
+ QItem,
115
+ QItemSection,
116
+ QCard,
117
+ QCardSection,
118
+ QCardActions,
119
+ QDialog,
120
+ QLinearProgress,
121
+ QTooltip,
122
+ QCheckbox,
123
+ QSpinnerGears,
124
+ QSpinnerDots,
125
+ QPagination,
126
+ QExpansionItem
127
+ },
128
+ directives: {
129
+ ClosePopup,
130
+ Ripple
131
+ },
132
+ lang: quasarLangEN
133
+ })
134
+ Dark.set(false)
135
+
136
+ app.component('View', View)
137
+
138
+ window.Vue = Vue
139
+ window.vueInstance = app
140
+ window.webb = (Webb as unknown as any)
141
+
142
+ if (!window.webb.getUrl) {
143
+ window.webb.getUrl = (): { webbHost: string } => ({
144
+ webbHost: import.meta.env.VITE_REQUEST_URL || `${window.location.protocol}//${window.location.host}`
145
+ })
146
+ }
147
+ if (!window.webb.ui) {
148
+ window.webb.ui = {
149
+ message (opts: any): void {
150
+ const type = opts?.type === 'error' ? 'negative' : opts?.type === 'success' ? 'positive' : 'info'
151
+ Notify.create({ type, message: opts?.message || '', position: 'top', timeout: 3000 })
152
+ }
153
+ }
154
+ }
155
+ if (!window.webb.method) {
156
+ window.webb.method = {
157
+ call (_module: string, _method: string, ..._args: any[]): any {
158
+ console.log(`[dev mock] webb.method.call(${_module}, ${_method})`, _args)
159
+ return {}
160
+ }
161
+ }
162
+ }
163
+
164
+ // ==================== Mock: window.xnb ====================
165
+
166
+ const DEV_TOKEN = import.meta.env.VITE_DEV_TOKEN || ''
167
+
168
+ const ResponseType = { JSON: 1, Text: 2, Binary: 3 }
169
+
170
+ const Body = {
171
+ json: (data: any): { type: string; data: any } => ({ type: 'json', data }),
172
+ text: (text: string): { type: string; data: string } => ({ type: 'text', data: text }),
173
+ form: (data: Record<string, any>): { type: string; data: Record<string, any> } => ({ type: 'form', data })
174
+ }
175
+
176
+ const httpClient = {
177
+ async request (options: {
178
+ method: string;
179
+ url: string;
180
+ headers?: Record<string, string>;
181
+ responseType?: number;
182
+ query?: Record<string, string | number | boolean | string[] | null | undefined>;
183
+ body?: { type: string; data: any };
184
+ }): Promise<any> {
185
+ const { method, url, headers, query, body } = options
186
+
187
+ const axiosConfig: Record<string, any> = {
188
+ method: method.toLowerCase(),
189
+ url,
190
+ headers: headers || {}
191
+ }
192
+
193
+ if (query != null && Object.keys(query).length > 0) {
194
+ axiosConfig.params = query
195
+ }
196
+
197
+ if (body) {
198
+ axiosConfig.data = body.data
199
+ }
200
+
201
+ try {
202
+ const res = await axios(axiosConfig)
203
+ return {
204
+ data: res.data,
205
+ headers: res.headers as Record<string, string>
206
+ }
207
+ } catch (err: any) {
208
+ if (err.response) {
209
+ return {
210
+ data: err.response.data,
211
+ headers: err.response.headers as Record<string, string>
212
+ }
213
+ }
214
+ throw err
215
+ }
216
+ }
217
+ }
218
+
219
+ window.xnb = {
220
+ book: {
221
+ async getCell (): Promise<any> {
222
+ const _d = localStorage.getItem('CH_CELL_DATA')
223
+ return { data: _d ? JSON.parse(_d) : {} }
224
+ },
225
+ async updateCellData (_bookPath: string, _cellUid: string, data: any): Promise<void> {
226
+ localStorage.setItem('CH_CELL_DATA', JSON.stringify(data))
227
+ },
228
+ async getBookMeta (_bookPath: string): Promise<any> {
229
+ return {
230
+ name: 'DevBook',
231
+ runtimes: {
232
+ bookId: 'dev-book-001',
233
+ project: { data_link: '', project_id: 'dev-project-001', project_name: 'Dev Project' }
234
+ }
235
+ }
236
+ },
237
+ async getBookFromRemoteRepo (_bookId: string): Promise<any> {
238
+ return { parent: { id: 1, name: 'DevFolder' }, creator: 'DevUser', name: 'DevBook', page_number: 1 }
239
+ },
240
+ async getCurrentBookPermission (_type: string): Promise<boolean> {
241
+ return true
242
+ }
243
+ },
244
+
245
+ store,
246
+ event: window.webb?.event || { on: (): void => {}, emit: (): void => {}, off: (): void => {} },
247
+
248
+ kernel: {
249
+ async runCode (_bookPath: string, _code: string, onUpdate: any): Promise<void> {
250
+ onUpdate('status: 200')
251
+ }
252
+ },
253
+
254
+ choreo: {
255
+ async getUserToken (_bookPath?: string): Promise<string> {
256
+ if (!DEV_TOKEN) {
257
+ console.warn('[dev mock] VITE_DEV_TOKEN 未在 .env.local 中配置')
258
+ }
259
+ return DEV_TOKEN
260
+ },
261
+ async getUserInfo (_options?: any): Promise<{ userName: string; email: string }> {
262
+ return { userName: 'DevUser', email: 'dev@local.test' }
263
+ }
264
+ },
265
+
266
+ http: { client: httpClient, Body, ResponseType },
267
+
268
+ app: {
269
+ alarm (info: string, _options?: any): void {
270
+ Notify.create({ type: 'warning', message: `[Alarm] ${info}`, position: 'top' })
271
+ },
272
+ notifySuccess (content: string, _options?: any): void {
273
+ Notify.create({ type: 'positive', message: content, position: 'top' })
274
+ }
275
+ },
276
+
277
+ utils: { dayjs, lodash }
278
+ }
279
+
280
+ app.mount('#app')
281
+
282
+ window.$q = {
283
+ loading: {
284
+ show (opts?: any): void {
285
+ Loading.show(typeof opts === 'string' ? { message: opts } : opts)
286
+ },
287
+ hide (): void {
288
+ Loading.hide()
289
+ }
290
+ },
291
+ notify: Notify.create.bind(Notify),
292
+ dialog: Dialog.create.bind(Dialog),
293
+ dark: Dark
294
+ }
295
+
296
+ axios.interceptors.request.use((config: any) => {
297
+ if (config.headers?.Authorization) return config
298
+ const token = DEV_TOKEN || localStorage.getItem('access_token')
299
+ if (token && config.headers) {
300
+ config.headers.Authorization = `Bearer ${token}`
301
+ }
302
+ return config
303
+ }, (err: any) => {
304
+ return Promise.reject(err)
305
+ })