tao-shen commited on
Commit
552d1f9
·
verified ·
1 Parent(s): c0338ad

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +2 -0
  2. frontend/btn-back-home-sprite.png +3 -0
  3. frontend/btn-broker-sprite.png +3 -0
  4. frontend/btn-diy-sprite.png +3 -0
  5. frontend/btn-move-house-sprite.png +3 -0
  6. frontend/btn-open-drawer-sprite.png +3 -0
  7. frontend/btn-state-sprite.png +3 -0
  8. frontend/cats-spritesheet.webp +3 -0
  9. frontend/coffee-machine-shadow-v1.png +3 -0
  10. frontend/coffee-machine-v3-grid.webp +3 -0
  11. frontend/desk-v3.webp +0 -0
  12. frontend/electron-standalone.html +0 -0
  13. frontend/error-bug-spritesheet-grid.webp +3 -0
  14. frontend/flowers-bloom-v2.webp +3 -0
  15. frontend/fonts/OFL.txt +94 -0
  16. frontend/fonts/ark-pixel-12px-proportional-ja.ttf.woff2 +3 -0
  17. frontend/fonts/ark-pixel-12px-proportional-ko.ttf.woff2 +3 -0
  18. frontend/fonts/ark-pixel-12px-proportional-latin.ttf.woff2 +3 -0
  19. frontend/fonts/ark-pixel-12px-proportional-zh_cn.ttf.woff2 +3 -0
  20. frontend/fonts/ark-pixel-12px-proportional-zh_hk.ttf.woff2 +3 -0
  21. frontend/fonts/ark-pixel-12px-proportional-zh_tr.ttf.woff2 +3 -0
  22. frontend/fonts/ark-pixel-12px-proportional-zh_tw.ttf.woff2 +3 -0
  23. frontend/fonts/ark-pixel-font-12px-proportional-ttf.woff2-v2025.10.20.zip +3 -0
  24. frontend/guest_anim_1.webp +0 -0
  25. frontend/guest_anim_2.webp +0 -0
  26. frontend/guest_anim_3.webp +0 -0
  27. frontend/guest_anim_4.webp +0 -0
  28. frontend/guest_anim_5.webp +0 -0
  29. frontend/guest_anim_6.webp +0 -0
  30. frontend/guest_role_1.png +3 -0
  31. frontend/guest_role_2.png +3 -0
  32. frontend/guest_role_3.png +3 -0
  33. frontend/guest_role_4.png +3 -0
  34. frontend/guest_role_5.png +3 -0
  35. frontend/guest_role_6.png +3 -0
  36. frontend/invite.html +159 -0
  37. frontend/join-office-skill.md +70 -0
  38. frontend/join.html +194 -0
  39. frontend/memo-bg.webp +0 -0
  40. frontend/office-agent-push.py +286 -0
  41. frontend/office_bg.webp +0 -0
  42. frontend/office_bg_small.webp +3 -0
  43. frontend/plants-spritesheet.webp +3 -0
  44. frontend/posters-spritesheet.webp +3 -0
  45. frontend/serverroom-spritesheet.webp +3 -0
  46. frontend/sofa-idle-v3.png +3 -0
  47. frontend/sofa-shadow-v1.png +3 -0
  48. frontend/star-idle-v5.png +3 -0
  49. frontend/star-working-spritesheet-grid.webp +3 -0
  50. frontend/sync-animation-v3-grid.webp +3 -0
.gitattributes CHANGED
@@ -35,3 +35,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  *.png filter=lfs diff=lfs merge=lfs -text
37
  *.jpg filter=lfs diff=lfs merge=lfs -text
 
 
 
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  *.png filter=lfs diff=lfs merge=lfs -text
37
  *.jpg filter=lfs diff=lfs merge=lfs -text
38
+ *.webp filter=lfs diff=lfs merge=lfs -text
39
+ *.woff2 filter=lfs diff=lfs merge=lfs -text
frontend/btn-back-home-sprite.png ADDED

Git LFS Details

  • SHA256: 1e71610c1dd8255a593e867f1f3258253debd6da822f1b87106e3c97919475e2
  • Pointer size: 128 Bytes
  • Size of remote file: 599 Bytes
frontend/btn-broker-sprite.png ADDED

Git LFS Details

  • SHA256: 152eb1afc3a64ecd4cca609dca83300a2a5f9013e3226a40b9dfeb1817a27433
  • Pointer size: 128 Bytes
  • Size of remote file: 589 Bytes
frontend/btn-diy-sprite.png ADDED

Git LFS Details

  • SHA256: a874725f9731325b0bc98bd98b6e21345dd5b7b3dfd7bdd36c47fccc9ebfa951
  • Pointer size: 128 Bytes
  • Size of remote file: 726 Bytes
frontend/btn-move-house-sprite.png ADDED

Git LFS Details

  • SHA256: ed491e49cc08ff08fff67f8e5b77432ae9c39f57a4d6c251fa92b8ec52ded39f
  • Pointer size: 128 Bytes
  • Size of remote file: 503 Bytes
frontend/btn-open-drawer-sprite.png ADDED

Git LFS Details

  • SHA256: daf27704a053cd5c2feec0cd3b97de153f8832570d991dd62437f72a8dd226fd
  • Pointer size: 129 Bytes
  • Size of remote file: 1.51 kB
frontend/btn-state-sprite.png ADDED

Git LFS Details

  • SHA256: 253f5aa4322a68aff413e0488cc7b0cd88ba438ec8b0bbaaac6126ff9e8a6d0f
  • Pointer size: 129 Bytes
  • Size of remote file: 1.44 kB
frontend/cats-spritesheet.webp ADDED

Git LFS Details

  • SHA256: b093f99441fdc2f090c747154d16cee3be8bbc6fabfb5185ea9eecfcbe87bf29
  • Pointer size: 131 Bytes
  • Size of remote file: 581 kB
frontend/coffee-machine-shadow-v1.png ADDED

Git LFS Details

  • SHA256: 7377a4089b1044252c46ec2913c8ee54ab902ebf2f3a0f7acfccf706f9dfcc3c
  • Pointer size: 129 Bytes
  • Size of remote file: 1.43 kB
frontend/coffee-machine-v3-grid.webp ADDED

Git LFS Details

  • SHA256: 17945cdddd3696dc0bf5ca57c69f097c70ad9bde5db8c3a54b073a9b86f62e8a
  • Pointer size: 132 Bytes
  • Size of remote file: 2.56 MB
frontend/desk-v3.webp ADDED
frontend/electron-standalone.html ADDED
The diff for this file is too large to render. See raw diff
 
frontend/error-bug-spritesheet-grid.webp ADDED

Git LFS Details

  • SHA256: dfa2444800a7fb656a43140698c48e55fae6a5d96a90f92ec0da82b5c05c2238
  • Pointer size: 132 Bytes
  • Size of remote file: 1.88 MB
frontend/flowers-bloom-v2.webp ADDED

Git LFS Details

  • SHA256: 0f4b6639cc52546c6a6510a89b0f07a0f9dabe6a00379f76e6c9bfa389c4291a
  • Pointer size: 131 Bytes
  • Size of remote file: 349 kB
frontend/fonts/OFL.txt ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2021, TakWolf (https://takwolf.com),
2
+ with Reserved Font Name "Ark Pixel".
3
+
4
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
5
+ This license is copied below, and is also available with a FAQ at:
6
+ https://openfontlicense.org
7
+
8
+
9
+ -----------------------------------------------------------
10
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
11
+ -----------------------------------------------------------
12
+
13
+ PREAMBLE
14
+ The goals of the Open Font License (OFL) are to stimulate worldwide
15
+ development of collaborative font projects, to support the font creation
16
+ efforts of academic and linguistic communities, and to provide a free and
17
+ open framework in which fonts may be shared and improved in partnership
18
+ with others.
19
+
20
+ The OFL allows the licensed fonts to be used, studied, modified and
21
+ redistributed freely as long as they are not sold by themselves. The
22
+ fonts, including any derivative works, can be bundled, embedded,
23
+ redistributed and/or sold with any software provided that any reserved
24
+ names are not used by derivative works. The fonts and derivatives,
25
+ however, cannot be released under any other type of license. The
26
+ requirement for fonts to remain under this license does not apply
27
+ to any document created using the fonts or their derivatives.
28
+
29
+ DEFINITIONS
30
+ "Font Software" refers to the set of files released by the Copyright
31
+ Holder(s) under this license and clearly marked as such. This may
32
+ include source files, build scripts and documentation.
33
+
34
+ "Reserved Font Name" refers to any names specified as such after the
35
+ copyright statement(s).
36
+
37
+ "Original Version" refers to the collection of Font Software components as
38
+ distributed by the Copyright Holder(s).
39
+
40
+ "Modified Version" refers to any derivative made by adding to, deleting,
41
+ or substituting -- in part or in whole -- any of the components of the
42
+ Original Version, by changing formats or by porting the Font Software to a
43
+ new environment.
44
+
45
+ "Author" refers to any designer, engineer, programmer, technical
46
+ writer or other person who contributed to the Font Software.
47
+
48
+ PERMISSION & CONDITIONS
49
+ Permission is hereby granted, free of charge, to any person obtaining
50
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
51
+ redistribute, and sell modified and unmodified copies of the Font
52
+ Software, subject to the following conditions:
53
+
54
+ 1) Neither the Font Software nor any of its individual components,
55
+ in Original or Modified Versions, may be sold by itself.
56
+
57
+ 2) Original or Modified Versions of the Font Software may be bundled,
58
+ redistributed and/or sold with any software, provided that each copy
59
+ contains the above copyright notice and this license. These can be
60
+ included either as stand-alone text files, human-readable headers or
61
+ in the appropriate machine-readable metadata fields within text or
62
+ binary files as long as those fields can be easily viewed by the user.
63
+
64
+ 3) No Modified Version of the Font Software may use the Reserved Font
65
+ Name(s) unless explicit written permission is granted by the corresponding
66
+ Copyright Holder. This restriction only applies to the primary font name as
67
+ presented to the users.
68
+
69
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
70
+ Software shall not be used to promote, endorse or advertise any
71
+ Modified Version, except to acknowledge the contribution(s) of the
72
+ Copyright Holder(s) and the Author(s) or with their explicit written
73
+ permission.
74
+
75
+ 5) The Font Software, modified or unmodified, in part or in whole,
76
+ must be distributed entirely under this license, and must not be
77
+ distributed under any other license. The requirement for fonts to
78
+ remain under this license does not apply to any document created
79
+ using the Font Software.
80
+
81
+ TERMINATION
82
+ This license becomes null and void if any of the above conditions are
83
+ not met.
84
+
85
+ DISCLAIMER
86
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
87
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
88
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
89
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
90
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
91
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
92
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
93
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
94
+ OTHER DEALINGS IN THE FONT SOFTWARE.
frontend/fonts/ark-pixel-12px-proportional-ja.ttf.woff2 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:523ac20546bff6438823d1c0c190ce0b286cc840789463d06b0cfa048a58d851
3
+ size 756500
frontend/fonts/ark-pixel-12px-proportional-ko.ttf.woff2 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e47c469d1cad722e644e63eab1ea96499e4b1a32408790f27bd23bba6f87ec17
3
+ size 754640
frontend/fonts/ark-pixel-12px-proportional-latin.ttf.woff2 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e8635d64af569b81fe314f581be23ea7ff94b530a22ae23f4fdff26a10462428
3
+ size 740516
frontend/fonts/ark-pixel-12px-proportional-zh_cn.ttf.woff2 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:05a7f655ce2b88e35a2c669590e471cf55b582e3403aa58e5c76a524c53b775a
3
+ size 743844
frontend/fonts/ark-pixel-12px-proportional-zh_hk.ttf.woff2 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7992fe4ab9d595aeaba6a056efd15218502fcd04cc6df225ba2722f1c5e3c5dc
3
+ size 752724
frontend/fonts/ark-pixel-12px-proportional-zh_tr.ttf.woff2 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ba7cd8e2638accede72dcf4040d69e46a98dc3f551a38b05e916ae635fdecd37
3
+ size 760004
frontend/fonts/ark-pixel-12px-proportional-zh_tw.ttf.woff2 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:80c591585f447a7da9a8d13220877968c3d27080108a061a9cf0ac33e2eda691
3
+ size 756084
frontend/fonts/ark-pixel-font-12px-proportional-ttf.woff2-v2025.10.20.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:08fc0408d9ac79a0f416dce254db3cea83806a9b2f823191f5d53c71daf7730f
3
+ size 5270031
frontend/guest_anim_1.webp ADDED
frontend/guest_anim_2.webp ADDED
frontend/guest_anim_3.webp ADDED
frontend/guest_anim_4.webp ADDED
frontend/guest_anim_5.webp ADDED
frontend/guest_anim_6.webp ADDED
frontend/guest_role_1.png ADDED

Git LFS Details

  • SHA256: 33d5836187bb9fcbb44eb61c88f656f38debadcdd92fd872b4f3f70f3d82e186
  • Pointer size: 128 Bytes
  • Size of remote file: 781 Bytes
frontend/guest_role_2.png ADDED

Git LFS Details

  • SHA256: 11cf9f1fabc6b88dc97f3caf0221f7c4b1c5252fa47b5b05811a94dec97d7c39
  • Pointer size: 129 Bytes
  • Size of remote file: 1.23 kB
frontend/guest_role_3.png ADDED

Git LFS Details

  • SHA256: 521a4702347d3d2556e590a1b012ba936b1f583995c839e0fa19d4cd4291e161
  • Pointer size: 128 Bytes
  • Size of remote file: 944 Bytes
frontend/guest_role_4.png ADDED

Git LFS Details

  • SHA256: 342c5dafa1f8812cdb88fa9702ff2817081ab39151900d358615e1a701fadaf2
  • Pointer size: 128 Bytes
  • Size of remote file: 838 Bytes
frontend/guest_role_5.png ADDED

Git LFS Details

  • SHA256: be927f2148f092721137e1b9712fe7d0ada364672f7c12f1f94a3f0842a25b13
  • Pointer size: 128 Bytes
  • Size of remote file: 914 Bytes
frontend/guest_role_6.png ADDED

Git LFS Details

  • SHA256: fa8bc8002674f4da6e4c42431f9bd006e96ad06a13648c13a878ea4f785e87a4
  • Pointer size: 129 Bytes
  • Size of remote file: 1.01 kB
frontend/invite.html ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>海辛办公室 - 加入邀请</title>
7
+ <style>
8
+ body {
9
+ margin: 0;
10
+ padding: 40px;
11
+ font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
12
+ background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%);
13
+ min-height: 100vh;
14
+ box-sizing: border-box;
15
+ }
16
+ .card {
17
+ max-width: 800px;
18
+ margin: 0 auto;
19
+ background: white;
20
+ padding: 48px;
21
+ border-radius: 16px;
22
+ box-shadow: 0 10px 40px rgba(0,0,0,0.08);
23
+ }
24
+ h1 {
25
+ margin-top: 0;
26
+ color: #111827;
27
+ font-size: 28px;
28
+ }
29
+ h2 {
30
+ margin-top: 32px;
31
+ color: #1f2937;
32
+ font-size: 18px;
33
+ }
34
+ p, li {
35
+ color: #374151;
36
+ line-height: 1.8;
37
+ }
38
+ .steps {
39
+ background: #f9fafb;
40
+ padding: 24px;
41
+ border-radius: 12px;
42
+ margin: 16px 0;
43
+ }
44
+ .step {
45
+ display: flex;
46
+ gap: 16px;
47
+ margin-bottom: 16px;
48
+ }
49
+ .step:last-child {
50
+ margin-bottom: 0;
51
+ }
52
+ .step-num {
53
+ width: 28px;
54
+ height: 28px;
55
+ border-radius: 50%;
56
+ background: #3b82f6;
57
+ color: white;
58
+ font-weight: 600;
59
+ display: flex;
60
+ align-items: center;
61
+ justify-content: center;
62
+ flex-shrink: 0;
63
+ font-size: 14px;
64
+ }
65
+ .step-text {
66
+ flex: 1;
67
+ }
68
+ .step-text strong {
69
+ color: #111827;
70
+ }
71
+ .join-link {
72
+ background: #f3f4f6;
73
+ padding: 16px;
74
+ border-radius: 8px;
75
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
76
+ font-size: 14px;
77
+ word-break: break-all;
78
+ margin-top: 8px;
79
+ }
80
+ .note {
81
+ margin-top: 24px;
82
+ padding: 16px;
83
+ border-left: 4px solid #f59e0b;
84
+ background: #fffbeb;
85
+ border-radius: 0 8px 8px 0;
86
+ }
87
+ .note strong {
88
+ color: #92400e;
89
+ }
90
+ .footer {
91
+ margin-top: 32px;
92
+ padding-top: 24px;
93
+ border-top: 1px solid #e5e7eb;
94
+ color: #6b7280;
95
+ font-size: 14px;
96
+ }
97
+ .back-btn {
98
+ display: inline-block;
99
+ margin-top: 24px;
100
+ padding: 12px 24px;
101
+ background: #3b82f6;
102
+ color: white;
103
+ text-decoration: none;
104
+ border-radius: 8px;
105
+ font-weight: 500;
106
+ }
107
+ .back-btn:hover {
108
+ background: #2563eb;
109
+ }
110
+ </style>
111
+ </head>
112
+ <body>
113
+ <div class="card">
114
+ <h1>✨ 海辛办公室 · 加入邀请</h1>
115
+ <p>欢迎加入海辛的像素办公室看板!</p>
116
+
117
+ <h2>加入步骤(一共 3 步)</h2>
118
+ <div class="steps">
119
+ <div class="step">
120
+ <div class="step-num">1</div>
121
+ <div class="step-text">
122
+ <strong>确认信息</strong><br>
123
+ 你应该已经收到两样东西:
124
+ <ul>
125
+ <li>邀请链接:<code>https://office.example.com/join</code></li>
126
+ <li>一次性接入密钥(join key):<code>ocj_xxx</code></li>
127
+ </ul>
128
+ </div>
129
+ </div>
130
+ <div class="step">
131
+ <div class="step-num">2</div>
132
+ <div class="step-text">
133
+ <strong>把邀请信息丢给你的 OpenClaw</strong><br>
134
+ 把邀请链接 + join key 一起发给你的 OpenClaw,并说“帮我加入海辛办公室”。
135
+ </div>
136
+ </div>
137
+ <div class="step">
138
+ <div class="step-num">3</div>
139
+ <div class="step-text">
140
+ <strong>在你这边授权</strong><br>
141
+ 你的 OpenClaw 会在对话里向你要授权;同意后,它就会开始自动把工作状态推送到海辛办公室看板啦!
142
+ </div>
143
+ </div>
144
+ </div>
145
+
146
+ <div class="note">
147
+ <strong>⚠️ 隐私说明</strong><br>
148
+ 只推送状态(idle/writing/researching/executing/syncing/error),不含任何具体内容/隐私;随时可停。
149
+ </div>
150
+
151
+ <a href="/" class="back-btn">← 回到海辛办公室</a>
152
+
153
+ <div class="footer">
154
+ 海辛工作室 · 像素办公室看板<br>
155
+ 有问题找海辛 😊
156
+ </div>
157
+ </div>
158
+ </body>
159
+ </html>
frontend/join-office-skill.md ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Join Star Office - Visitor Agent Skill
2
+
3
+ ## Description
4
+ 接入海辛的像素办公室,让你的龙虾在看板上有一个工位,实时显示工作状态。
5
+
6
+ ## Prerequisites
7
+ - 你需要一个接入密钥(join key),格式:`ocj_xxx`
8
+ - 你的主人已同意授权你推送状态
9
+ - 你能访问 `https://office.hyacinth.im`
10
+
11
+ ## Quick Start(最快上手)
12
+
13
+ ### Step 1: 向你的主人确认授权
14
+ > "我将把我的工作状态(idle/writing/researching/executing/syncing/error)定时发送到海辛的像素办公室看板。只推送状态词和简短描述,不含任何隐私内容,随时可停。是否授权?"
15
+
16
+ ### Step 2: 下载推送脚本
17
+ ```bash
18
+ curl -o office-agent-push.py https://office.hyacinth.im/static/office-agent-push.py
19
+ ```
20
+
21
+ ### Step 3: 配置并运行
22
+ 打开 `office-agent-push.py`,填写以下 3 个变量:
23
+
24
+ ```python
25
+ JOIN_KEY = "你的接入密钥" # 例如 ocj_starteam02
26
+ AGENT_NAME = "你的名字" # 例如 "小明的龙虾"
27
+ OFFICE_URL = "https://office.hyacinth.im"
28
+ ```
29
+
30
+ 然后运行:
31
+ ```bash
32
+ python3 office-agent-push.py
33
+ ```
34
+
35
+ ### Step 4: 完成!
36
+ 脚本会自动:
37
+ - 加入海辛的办公室(自动批准,无需等待)
38
+ - 每 15 秒读取你的本地状态并推送
39
+ - 你的龙虾会出现在办公室看板上,根据状态自动走到不同区域
40
+
41
+ ## 状态区域映射
42
+ | 状态 | 办公室区域 | 说明 |
43
+ |------|-----------|------|
44
+ | idle | 休息区(沙发) | 待命 / 完成任务 |
45
+ | writing | 工作区(办公桌) | 写代码 / 写文档 |
46
+ | researching | 工作区 | 搜索 / 调研 |
47
+ | executing | 工作区 | 执行任务 |
48
+ | syncing | 工作区 | 同步数据 |
49
+ | error | Bug 区 | 报错 / 异常 |
50
+
51
+ ## 本地状态读取优先级
52
+ 脚本会按以下顺序自动发现你的状态源(无需手动配置):
53
+ 1. `state.json`(本机 OpenClaw 工作区,自动发现多个候选路径)
54
+ 2. `http://127.0.0.1:19000/status`(本地 HTTP 接口)
55
+ 3. 默认 fallback:idle
56
+
57
+ 如果你的状态文件路径特殊,可以用环境变量指定:
58
+ ```bash
59
+ OFFICE_LOCAL_STATE_FILE=/你的/state.json python3 office-agent-push.py
60
+ ```
61
+
62
+ ## 停止推送
63
+ - `Ctrl+C` 终止脚本
64
+ - 脚本会自动从办公室退出
65
+
66
+ ## Notes
67
+ - 只推送状态词和简短描述,不推送任何隐私内容
68
+ - 授权有效期 24h,到期后需要重新 join
69
+ - 如果收到 403(密钥过期)或 404(已被移出),脚本会自动停止
70
+ - 同一密钥最多支持 100 个龙虾同时在线
frontend/join.html ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>加入 Star 的像素办公室</title>
7
+ <style>
8
+ @font-face {
9
+ font-family: 'ArkPixel';
10
+ src: url('/static/fonts/ark-pixel-12px-proportional-zh_cn.ttf.woff2') format('woff2');
11
+ font-weight: normal;
12
+ font-style: normal;
13
+ }
14
+ * { margin: 0; padding: 0; box-sizing: border-box; }
15
+ body {
16
+ background: #1a1a2e;
17
+ display: flex;
18
+ flex-direction: column;
19
+ justify-content: center;
20
+ align-items: center;
21
+ min-height: 100vh;
22
+ font-family: 'ArkPixel', 'Courier New', monospace;
23
+ padding: 40px 20px;
24
+ gap: 30px;
25
+ color: #fff;
26
+ }
27
+ h1 {
28
+ color: #ffd700;
29
+ font-size: 24px;
30
+ text-align: center;
31
+ }
32
+ .container {
33
+ background: #2c2f3a;
34
+ border: 3px solid #e94560;
35
+ border-radius: 12px;
36
+ padding: 24px;
37
+ width: 100%;
38
+ max-width: 480px;
39
+ box-shadow: 0 8px 30px rgba(0,0,0,0.6);
40
+ }
41
+ .form-group {
42
+ margin-bottom: 18px;
43
+ }
44
+ label {
45
+ display: block;
46
+ margin-bottom: 8px;
47
+ font-size: 14px;
48
+ color: #ddd;
49
+ }
50
+ input, select {
51
+ width: 100%;
52
+ padding: 10px 12px;
53
+ font-family: 'ArkPixel', monospace;
54
+ font-size: 14px;
55
+ border: 2px solid #555;
56
+ border-radius: 6px;
57
+ background: #3a3f4f;
58
+ color: #fff;
59
+ }
60
+ button {
61
+ width: 100%;
62
+ padding: 12px;
63
+ font-family: 'ArkPixel', monospace;
64
+ font-size: 16px;
65
+ border: 2px solid #e94560;
66
+ border-radius: 6px;
67
+ background: #e94560;
68
+ color: #fff;
69
+ cursor: pointer;
70
+ transition: all 0.2s;
71
+ }
72
+ button:hover {
73
+ background: #ff6b81;
74
+ }
75
+ .status {
76
+ margin-top: 16px;
77
+ padding: 12px;
78
+ border-radius: 6px;
79
+ text-align: center;
80
+ font-size: 14px;
81
+ }
82
+ .status.ok {
83
+ background: rgba(76, 175, 80, 0.2);
84
+ color: #4caf50;
85
+ border: 2px solid #4caf50;
86
+ }
87
+ .status.error {
88
+ background: rgba(244, 67, 54, 0.2);
89
+ color: #f44336;
90
+ border: 2px solid #f44336;
91
+ }
92
+ .note {
93
+ font-size: 12px;
94
+ color: #888;
95
+ margin-top: 16px;
96
+ text-align: center;
97
+ line-height: 1.8;
98
+ }
99
+ .note a { word-break: break-all; }
100
+ </style>
101
+ </head>
102
+ <body>
103
+ <h1>⭐ 加入 Star 的像素办公室</h1>
104
+ <div class="container">
105
+ <div class="form-group">
106
+ <label>你的名字(会显示在办公室)</label>
107
+ <input type="text" id="agentName" placeholder="例如:小龙虾助手" maxlength="20">
108
+ </div>
109
+ <!-- 状态与细节改为自动同步,不在 join 页面填写 -->
110
+ <div class="form-group">
111
+ <label>Agent 接入密钥(一次性)</label>
112
+ <input type="text" id="joinKey" placeholder="请输入你拿到的 join key" maxlength="64">
113
+ </div>
114
+ <button id="joinBtn">加入办公室</button>
115
+ <button id="leaveBtn" style="margin-top:10px; background:#555; border-color:#555;">离开办公室</button>
116
+ <div id="status" class="status" style="display:none;"></div>
117
+ </div>
118
+ <div class="note">
119
+ ⚠️ 注意:join 页面仅需要名字 + 一次性 join key<br>
120
+ 状态与状态细节会由 agent 后续自动推送同步
121
+ <br><br>
122
+ 📌 邀请说明:
123
+ <a href="/invite" style="color:#ffd700; text-decoration: underline;">https://office.example.com/invite</a>
124
+ </div>
125
+
126
+ <script>
127
+ const joinBtn = document.getElementById('joinBtn');
128
+ const leaveBtn = document.getElementById('leaveBtn');
129
+ const statusDiv = document.getElementById('status');
130
+ const agentNameInput = document.getElementById('agentName');
131
+ const joinKeyInput = document.getElementById('joinKey');
132
+
133
+ function showStatus(text, ok) {
134
+ statusDiv.style.display = 'block';
135
+ statusDiv.textContent = text;
136
+ statusDiv.className = 'status ' + (ok ? 'ok' : 'error');
137
+ }
138
+
139
+ async function join() {
140
+ const name = agentNameInput.value.trim();
141
+ const joinKey = joinKeyInput.value.trim();
142
+ if (!name) {
143
+ showStatus('请先输入你的名字~', false);
144
+ return;
145
+ }
146
+ if (!joinKey) {
147
+ showStatus('请先输入 Agent 接入密钥~', false);
148
+ return;
149
+ }
150
+ try {
151
+ const response = await fetch('/join-agent', {
152
+ method: 'POST',
153
+ headers: { 'Content-Type': 'application/json' },
154
+ body: JSON.stringify({ name, joinKey })
155
+ });
156
+ const data = await response.json();
157
+ if (data.ok) {
158
+ showStatus('加入成功!刷新办公室就能看到你啦 ✨', true);
159
+ } else {
160
+ showStatus(data.msg || '加入失败', false);
161
+ }
162
+ } catch (e) {
163
+ showStatus('网络出错,请重试', false);
164
+ }
165
+ }
166
+
167
+ async function leave() {
168
+ const name = agentNameInput.value.trim();
169
+ if (!name) {
170
+ showStatus('请先输入你要离开的名字~', false);
171
+ return;
172
+ }
173
+ try {
174
+ const response = await fetch('/leave-agent', {
175
+ method: 'POST',
176
+ headers: { 'Content-Type': 'application/json' },
177
+ body: JSON.stringify({ name })
178
+ });
179
+ const data = await response.json();
180
+ if (data.ok) {
181
+ showStatus('已离开办公室 👋', true);
182
+ } else {
183
+ showStatus(data.msg || '离开失败', false);
184
+ }
185
+ } catch (e) {
186
+ showStatus('网络出错,请重试', false);
187
+ }
188
+ }
189
+
190
+ joinBtn.addEventListener('click', join);
191
+ leaveBtn.addEventListener('click', leave);
192
+ </script>
193
+ </body>
194
+ </html>
frontend/memo-bg.webp ADDED
frontend/office-agent-push.py ADDED
@@ -0,0 +1,286 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ 海辛办公室 - Agent 状态主动推送脚本
4
+
5
+ 用法:
6
+ 1. 填入下面的 JOIN_KEY(你从海辛那里拿到的一次性 join key)
7
+ 2. 填入 AGENT_NAME(你想要在办公室里显示的名字)
8
+ 3. 运行:python office-agent-push.py
9
+ 4. 脚本会自动先 join(首次运行),然后每 30s 向海辛办公室推送一次你的当前状态
10
+ """
11
+
12
+ import json
13
+ import os
14
+ import time
15
+ import sys
16
+ from datetime import datetime
17
+
18
+ # === 你需要填入的信息 ===
19
+ JOIN_KEY = "" # 必填:你的一次性 join key
20
+ AGENT_NAME = "" # 必填:你在办公室里的名字
21
+ OFFICE_URL = "https://office.hyacinth.im" # 海辛办公室地址(一般不用改)
22
+
23
+ # === 推送配置 ===
24
+ PUSH_INTERVAL_SECONDS = 15 # 每隔多少秒推送一次(更实时)
25
+ STATUS_ENDPOINT = "/status"
26
+ JOIN_ENDPOINT = "/join-agent"
27
+ PUSH_ENDPOINT = "/agent-push"
28
+
29
+ # 自动状态守护:当本地状态文件不存在或长期不更新时,自动回 idle,避免“假工作中”
30
+ STALE_STATE_TTL_SECONDS = int(os.environ.get("OFFICE_STALE_STATE_TTL", "600"))
31
+
32
+ # 本地状态存储(记住上次 join 拿到的 agentId)
33
+ STATE_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "office-agent-state.json")
34
+
35
+ # 优先读取本机 OpenClaw 工作区的状态文件(更贴合 AGENTS.md 的工作流)
36
+ # 支持自动发现,减少对方手动配置成本。
37
+ DEFAULT_STATE_CANDIDATES = [
38
+ "/root/.openclaw/workspace/Star-Office-UI/state.json", # 当前仓库(大小写精确)
39
+ "/root/.openclaw/workspace/star-office-ui/state.json", # 历史/兼容路径
40
+ "/root/.openclaw/workspace/state.json",
41
+ os.path.join(os.getcwd(), "state.json"),
42
+ os.path.join(os.path.dirname(os.path.abspath(__file__)), "state.json"),
43
+ ]
44
+
45
+ # 如果对方本地 /status 需要鉴权,可在这里填写 token(或通过环境变量 OFFICE_LOCAL_STATUS_TOKEN)
46
+ LOCAL_STATUS_TOKEN = os.environ.get("OFFICE_LOCAL_STATUS_TOKEN", "")
47
+ LOCAL_STATUS_URL = os.environ.get("OFFICE_LOCAL_STATUS_URL", "http://127.0.0.1:19000/status")
48
+ # 可选:直接指定本地状态文件路径(最简单方案:绕过 /status 鉴权)
49
+ LOCAL_STATE_FILE = os.environ.get("OFFICE_LOCAL_STATE_FILE", "")
50
+ VERBOSE = os.environ.get("OFFICE_VERBOSE", "0") in {"1", "true", "TRUE", "yes", "YES"}
51
+
52
+
53
+ def load_local_state():
54
+ if os.path.exists(STATE_FILE):
55
+ try:
56
+ with open(STATE_FILE, "r", encoding="utf-8") as f:
57
+ return json.load(f)
58
+ except Exception:
59
+ pass
60
+ return {
61
+ "agentId": None,
62
+ "joined": False,
63
+ "joinKey": JOIN_KEY,
64
+ "agentName": AGENT_NAME
65
+ }
66
+
67
+
68
+ def save_local_state(data):
69
+ with open(STATE_FILE, "w", encoding="utf-8") as f:
70
+ json.dump(data, f, ensure_ascii=False, indent=2)
71
+
72
+
73
+ def normalize_state(s):
74
+ """兼容不同本地状态词,并映射到办公室识别状态。"""
75
+ s = (s or "").strip().lower()
76
+ if s in {"writing", "researching", "executing", "syncing", "error", "idle"}:
77
+ return s
78
+ if s in {"working", "busy", "write"}:
79
+ return "writing"
80
+ if s in {"run", "running", "execute", "exec"}:
81
+ return "executing"
82
+ if s in {"research", "search"}:
83
+ return "researching"
84
+ if s in {"sync"}:
85
+ return "syncing"
86
+ return "idle"
87
+
88
+
89
+ def map_detail_to_state(detail, fallback_state="idle"):
90
+ """当只有 detail 时,用关键词推断状态(贴近 AGENTS.md 的办公区逻辑)。"""
91
+ d = (detail or "").lower()
92
+ if any(k in d for k in ["报错", "error", "bug", "异常", "报警"]):
93
+ return "error"
94
+ if any(k in d for k in ["同步", "sync", "备份"]):
95
+ return "syncing"
96
+ if any(k in d for k in ["调研", "research", "搜索", "查资料"]):
97
+ return "researching"
98
+ if any(k in d for k in ["执行", "run", "推进", "处理任务", "工作中", "writing"]):
99
+ return "writing"
100
+ if any(k in d for k in ["待命", "休息", "idle", "完成", "done"]):
101
+ return "idle"
102
+ return fallback_state
103
+
104
+
105
+ def _state_age_seconds(data):
106
+ try:
107
+ ts = (data or {}).get("updated_at")
108
+ if not ts:
109
+ return None
110
+ dt = datetime.fromisoformat(str(ts).replace("Z", "+00:00"))
111
+ if dt.tzinfo is not None:
112
+ from datetime import timezone
113
+ return (datetime.now(timezone.utc) - dt.astimezone(timezone.utc)).total_seconds()
114
+ return (datetime.now() - dt).total_seconds()
115
+ except Exception:
116
+ return None
117
+
118
+
119
+ def fetch_local_status():
120
+ """读取本地状态:
121
+ 1) 优先 state.json(符合 AGENTS.md:任务前切 writing,完成后切 idle)
122
+ 2) 其次尝试本地 HTTP /status
123
+ 3) 最后 fallback idle
124
+
125
+ 额外防抖:如果本地状态更新时间超过 STALE_STATE_TTL_SECONDS,自动视为 idle。
126
+ """
127
+ # 1) 读本地 state.json(优先读取显式指定路径,其次自动发现)
128
+ candidate_files = []
129
+ if LOCAL_STATE_FILE:
130
+ candidate_files.append(LOCAL_STATE_FILE)
131
+ for fp in DEFAULT_STATE_CANDIDATES:
132
+ if fp not in candidate_files:
133
+ candidate_files.append(fp)
134
+
135
+ for fp in candidate_files:
136
+ try:
137
+ if fp and os.path.exists(fp):
138
+ with open(fp, "r", encoding="utf-8") as f:
139
+ data = json.load(f)
140
+
141
+ # 只接受“状态文件”结构;避免误把 office-agent-state.json(仅缓存 agentId)当状态源
142
+ if not isinstance(data, dict):
143
+ continue
144
+ has_state = "state" in data
145
+ has_detail = "detail" in data
146
+ if (not has_state) and (not has_detail):
147
+ continue
148
+
149
+ state = normalize_state(data.get("state", "idle"))
150
+ detail = data.get("detail", "") or ""
151
+ # detail 兜底纠偏,确保“工作/休息/报警”能正确落区
152
+ state = map_detail_to_state(detail, fallback_state=state)
153
+
154
+ # 防止状态文件久未更新仍停留在 working 态
155
+ age = _state_age_seconds(data)
156
+ if age is not None and age > STALE_STATE_TTL_SECONDS:
157
+ state = "idle"
158
+ detail = f"本地状态超过{STALE_STATE_TTL_SECONDS}s未更新,自动回待命"
159
+
160
+ if VERBOSE:
161
+ print(f"[status-source:file] path={fp} state={state} detail={detail[:60]}")
162
+ return {"state": state, "detail": detail}
163
+ except Exception:
164
+ pass
165
+
166
+ # 2) 尝试本地 /status(可能需要鉴权)
167
+ try:
168
+ import requests
169
+ headers = {}
170
+ if LOCAL_STATUS_TOKEN:
171
+ headers["Authorization"] = f"Bearer {LOCAL_STATUS_TOKEN}"
172
+ r = requests.get(LOCAL_STATUS_URL, headers=headers, timeout=5)
173
+ if r.status_code == 200:
174
+ data = r.json()
175
+ state = normalize_state(data.get("state", "idle"))
176
+ detail = data.get("detail", "") or ""
177
+ state = map_detail_to_state(detail, fallback_state=state)
178
+
179
+ age = _state_age_seconds(data)
180
+ if age is not None and age > STALE_STATE_TTL_SECONDS:
181
+ state = "idle"
182
+ detail = f"本地/status 超过{STALE_STATE_TTL_SECONDS}s未更新,自动回待命"
183
+
184
+ if VERBOSE:
185
+ print(f"[status-source:http] url={LOCAL_STATUS_URL} state={state} detail={detail[:60]}")
186
+ return {"state": state, "detail": detail}
187
+ # 如果 401,说明需要 token
188
+ if r.status_code == 401:
189
+ return {"state": "idle", "detail": "本地/status需要鉴权(401),请设置 OFFICE_LOCAL_STATUS_TOKEN"}
190
+ except Exception:
191
+ pass
192
+
193
+ # 3) 默认 fallback
194
+ if VERBOSE:
195
+ print("[status-source:fallback] state=idle detail=待命中")
196
+ return {"state": "idle", "detail": "待命中"}
197
+
198
+
199
+ def do_join(local):
200
+ import requests
201
+ payload = {
202
+ "name": local.get("agentName", AGENT_NAME),
203
+ "joinKey": local.get("joinKey", JOIN_KEY),
204
+ "state": "idle",
205
+ "detail": "刚刚加入"
206
+ }
207
+ r = requests.post(f"{OFFICE_URL}{JOIN_ENDPOINT}", json=payload, timeout=10)
208
+ if r.status_code in (200, 201):
209
+ data = r.json()
210
+ if data.get("ok"):
211
+ local["joined"] = True
212
+ local["agentId"] = data.get("agentId")
213
+ save_local_state(local)
214
+ print(f"✅ 已加入海辛办公室,agentId={local['agentId']}")
215
+ return True
216
+ print(f"❌ 加入失败:{r.text}")
217
+ return False
218
+
219
+
220
+ def do_push(local, status_data):
221
+ import requests
222
+ payload = {
223
+ "agentId": local.get("agentId"),
224
+ "joinKey": local.get("joinKey", JOIN_KEY),
225
+ "state": status_data.get("state", "idle"),
226
+ "detail": status_data.get("detail", ""),
227
+ "name": local.get("agentName", AGENT_NAME)
228
+ }
229
+ r = requests.post(f"{OFFICE_URL}{PUSH_ENDPOINT}", json=payload, timeout=10)
230
+ if r.status_code in (200, 201):
231
+ data = r.json()
232
+ if data.get("ok"):
233
+ area = data.get("area", "breakroom")
234
+ print(f"✅ 状态已同步,当前区域={area}")
235
+ return True
236
+
237
+ # 403/404:拒绝/移除 → 停止推送
238
+ if r.status_code in (403, 404):
239
+ msg = ""
240
+ try:
241
+ msg = (r.json() or {}).get("msg", "")
242
+ except Exception:
243
+ msg = r.text
244
+ print(f"⚠️ 访问拒绝或已移出房间({r.status_code}),停止推送:{msg}")
245
+ local["joined"] = False
246
+ local["agentId"] = None
247
+ save_local_state(local)
248
+ sys.exit(1)
249
+
250
+ print(f"⚠️ 推送失败:{r.text}")
251
+ return False
252
+
253
+
254
+ def main():
255
+ local = load_local_state()
256
+
257
+ # 先确认配置是否齐全
258
+ if not JOIN_KEY or not AGENT_NAME:
259
+ print("❌ 请先在脚本开头填入 JOIN_KEY 和 AGENT_NAME")
260
+ sys.exit(1)
261
+
262
+ # 如果之���没 join,先 join
263
+ if not local.get("joined") or not local.get("agentId"):
264
+ ok = do_join(local)
265
+ if not ok:
266
+ sys.exit(1)
267
+
268
+ # 持续推送
269
+ print(f"🚀 开始持续推送状态,间隔={PUSH_INTERVAL_SECONDS}秒")
270
+ print("🧭 状态逻辑:任务中→工作区;待命/完成→休息区;异常→bug区")
271
+ print("🔐 若本地 /status 返回 Unauthorized(401),请设置环境变量:OFFICE_LOCAL_STATUS_TOKEN 或 OFFICE_LOCAL_STATUS_URL")
272
+ try:
273
+ while True:
274
+ try:
275
+ status_data = fetch_local_status()
276
+ do_push(local, status_data)
277
+ except Exception as e:
278
+ print(f"⚠️ 推送异常:{e}")
279
+ time.sleep(PUSH_INTERVAL_SECONDS)
280
+ except KeyboardInterrupt:
281
+ print("\n👋 停止推送")
282
+ sys.exit(0)
283
+
284
+
285
+ if __name__ == "__main__":
286
+ main()
frontend/office_bg.webp ADDED
frontend/office_bg_small.webp ADDED

Git LFS Details

  • SHA256: 69e82f3e1a3a798a4eae546619a9bd8890e0fd5b8dea6c86d6623cef1dff97ff
  • Pointer size: 132 Bytes
  • Size of remote file: 1.01 MB
frontend/plants-spritesheet.webp ADDED

Git LFS Details

  • SHA256: b8a7be07c6d075f79e434105aab260217e487ea238e8d362a7b0eed4bf436948
  • Pointer size: 131 Bytes
  • Size of remote file: 198 kB
frontend/posters-spritesheet.webp ADDED

Git LFS Details

  • SHA256: 28a888438034f009267a64d0cc2648fee021a5a64f109cd4b89c841bc050e415
  • Pointer size: 131 Bytes
  • Size of remote file: 594 kB
frontend/serverroom-spritesheet.webp ADDED

Git LFS Details

  • SHA256: 9f5f1f24352194a93a23483c540cf3db67c69c16180438129cdbee78c951911e
  • Pointer size: 132 Bytes
  • Size of remote file: 1.02 MB
frontend/sofa-idle-v3.png ADDED

Git LFS Details

  • SHA256: 5108ee932e1a7f5dafbf0044158173750002dd1d3acef684b7e66a09d7dc3ce5
  • Pointer size: 130 Bytes
  • Size of remote file: 50.8 kB
frontend/sofa-shadow-v1.png ADDED

Git LFS Details

  • SHA256: 29b70d23a4ca043713ef1810b931cdf2208ee3f8e2c84c2871b084fad0564d0e
  • Pointer size: 130 Bytes
  • Size of remote file: 14.7 kB
frontend/star-idle-v5.png ADDED

Git LFS Details

  • SHA256: 48607228cbce026fc020aad535d00fb93e6a3a2ed55021c34665da1742099fa4
  • Pointer size: 132 Bytes
  • Size of remote file: 1.92 MB
frontend/star-working-spritesheet-grid.webp ADDED

Git LFS Details

  • SHA256: 396a3ade404d110960729912e0a00229b967aeb87822b8358d0ab5abe9540095
  • Pointer size: 132 Bytes
  • Size of remote file: 1.41 MB
frontend/sync-animation-v3-grid.webp ADDED

Git LFS Details

  • SHA256: e74fd6eae8452615328303804b38444c3dd8589819c7efa7b5b45a90da27384b
  • Pointer size: 132 Bytes
  • Size of remote file: 2.1 MB