@my-life-buddies/cli-windowscompat 0.0.0-stage → 0.29.0-dev.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.
@@ -0,0 +1,41 @@
1
+ // 注入隔离的 Widget 文档;只允许向宿主发送固定方法,不提供身份、Token 或网络代理。
2
+ ((render) => {
3
+ let counter = 0, closed = false, gesture = 0;
4
+ const pending = new Map();
5
+ document.addEventListener('click', event => { if (event.isTrusted) gesture = Date.now(); }, true);
6
+ function request(method, params = {}) {
7
+ if (closed) return Promise.reject(new Error('小挂件已关闭'));
8
+ const requestId = `${render}-${++counter}`;
9
+ return new Promise((resolve, reject) => {
10
+ const timer = setTimeout(() => { pending.delete(requestId); reject(new Error('操作等待超时,请关闭后重新打开并核对数据')); }, 90000);
11
+ pending.set(requestId, {resolve, reject, timer});
12
+ parent.postMessage({channel:'buddy-widget-bridge', render, requestId, method, params}, '*');
13
+ });
14
+ }
15
+ window.addEventListener('message', event => {
16
+ const data = event.data;
17
+ if (event.source !== parent || data?.channel !== 'buddy-widget-reply' || data.render !== render) return;
18
+ const call = pending.get(data.requestId); if (!call) return;
19
+ pending.delete(data.requestId); clearTimeout(call.timer);
20
+ if (data.error) call.reject(Object.assign(new Error(data.error.message), {status:data.error.status, code:data.error.code}));
21
+ else call.resolve(data.value);
22
+ });
23
+ const records = Object.freeze({
24
+ list: module => request('records.list', {module}).then(value => value.records),
25
+ get: (module, recordId) => request('records.get', {module, recordId}).then(value => value.record),
26
+ create: (module, data) => request('records.create', {module, data}),
27
+ update: (module, recordId, data, options = {}) => request('records.update', {module, recordId, data, expectedRevision:options.expectedRevision}),
28
+ delete: (module, recordId, options = {}) => request('records.delete', {module, recordId, expectedRevision:options.expectedRevision}),
29
+ });
30
+ const media = Object.freeze({
31
+ url: mediaId => request('media.url', {mediaId}).then(value => value.url),
32
+ pickAndUpload: () => {
33
+ if (Date.now() - gesture > 1000) return Promise.reject(new Error('请点击按钮后选择图片'));
34
+ gesture = 0; return request('media.pickAndUpload');
35
+ },
36
+ });
37
+ Object.defineProperty(window, 'dadaWidget', { value: Object.freeze({records, media,
38
+ close: async () => { await request('close'); closed = true; },
39
+ }), configurable:false, writable:false });
40
+ window.addEventListener('load', () => parent.postMessage({channel:'buddy-widget-document', render, state:'ready'}, '*'), {once:true});
41
+ })(__WIDGET_RENDER__);
package/package.json CHANGED
@@ -1,6 +1,56 @@
1
1
  {
2
2
  "name": "@my-life-buddies/cli-windowscompat",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.29.0-dev.0",
4
+ "type": "module",
5
+ "description": "Windows compatibility lane CLI — macOS configuration validation only; Windows is not supported by this release",
6
+ "publishConfig": {
7
+ "access": "public",
8
+ "tag": "dev",
9
+ "registry": "https://registry.npmjs.org/"
10
+ },
11
+ "engines": {
12
+ "node": ">=24"
13
+ },
14
+ "os": [
15
+ "darwin"
16
+ ],
17
+ "bin": {
18
+ "buddy-cli-windowscompat": "dist/bin/buddy.js"
19
+ },
20
+ "files": [
21
+ "dist/bin",
22
+ "resources",
23
+ "README.md"
24
+ ],
25
+ "exports": {
26
+ "./package.json": "./package.json"
27
+ },
28
+ "dependencies": {
29
+ "acorn": "8.15.0",
30
+ "ajv": "^8.20.0",
31
+ "qrcode": "1.5.4",
32
+ "sharp": "0.35.4",
33
+ "tar-stream": "^3.2.1",
34
+ "ws": "8.21.3"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+ssh://git@code.devops.xiaohongshu.com/dada/cli.git",
39
+ "directory": "apps/cli"
40
+ },
41
+ "buddyCliLane": {
42
+ "schemaVersion": 1,
43
+ "id": "windowscompat",
44
+ "origin": "https://buddies-windowscompat.devops.sl.sit.xiaohongshu.com",
45
+ "command": "buddy-cli-windowscompat",
46
+ "runtimePackage": "@my-life-buddies/buddy-runtime-windowscompat",
47
+ "runtimeTag": "dev",
48
+ "runtimePrerelease": "dev",
49
+ "sourceVersion": "0.29.0-dev.0",
50
+ "distribution": "npm",
51
+ "packageName": "@my-life-buddies/cli-windowscompat",
52
+ "version": "0.29.0-dev.0",
53
+ "validationScope": "mac-config-only"
54
+ },
55
+ "gitHead": "f5cb4a8d84dc54a80d398b401a350536134dc9c1"
56
+ }
@@ -0,0 +1,10 @@
1
+ # CLI 分发资源
2
+
3
+ 此目录包含工程模板和品牌资源。Creator 与 Avatar 规则、工具和参考图均由独立 npm 资源包提供,不进入 CLI 安装包。
4
+
5
+ - Creator:`@my-life-buddies/buddy-creator-skill`,本次版本记录 `.buddy/creator/skill-lock.json`;每次进入或恢复都检查公共 npm latest,再复用或更新缓存。
6
+ - Avatar:`@my-life-buddies/avatar-creator-skill`,项目锁 `.buddy/meta/skill-lock.json`。仅生成头像时加载;已有图片确认和上传无需该包。
7
+
8
+ `src/init/skill-package.ts` 负责两者共用的下载、完整性校验和缓存,分别按 Creator 始终 latest、Avatar 固定版本的策略选择资源,各自协议由 `creator-package.ts` 与 `avatar-package.ts` 定义。安装禁用 npm 脚本。Avatar 已有缓存可离线准备;Creator 每次必须联网查证最新版,失败不回退旧版;平台身份操作仍需要联网。
9
+
10
+ `tests/fixtures/` 是测试输入,不参与构建。安装包验收使用独立提供的 Avatar 测试包和公共 npm Creator 包;可通过 BUDDY_AVATAR_TEST_SKILL_ROOT 指定解包后的真实头像包执行验收。
@@ -0,0 +1,49 @@
1
+ import { createConnection } from "node:net";
2
+
3
+ /** Protocol v1. Must be awaited before importing the Runtime or user factory. */
4
+ export async function connectManagedDev(lifecycle) {
5
+ const pipe = process.env.BUDDY_DEV_CONTROL_PIPE;
6
+ const token = process.env.BUDDY_DEV_CONTROL_TOKEN;
7
+ delete process.env.BUDDY_DEV_CONTROL_PIPE;
8
+ delete process.env.BUDDY_DEV_CONTROL_TOKEN;
9
+ delete process.env.BUDDY_DEV_CONTROL_MODULE;
10
+ if (!pipe || !/^[a-f0-9]{64}$/u.test(token ?? "")) throw new Error("Invalid Windows DEV control environment");
11
+ const socket = createConnection(pipe);
12
+ socket.setEncoding("utf8");
13
+ let buffer = "", accepted = false, finishing = false;
14
+ let accept, reject;
15
+ const handshake = new Promise((resolve, fail) => { accept = resolve; reject = fail; });
16
+ const abort = error => { reject(error); lifecycle.abort(error); };
17
+ const timer = setTimeout(() => { abort(new Error("Windows DEV handshake timed out")); socket.destroy(); }, 10_000);
18
+ socket.on("error", abort);
19
+ socket.on("close", () => { if (!finishing) abort(new Error("Windows DEV owner disconnected")); });
20
+ socket.on("connect", () => socket.write(JSON.stringify({ type: "hello", protocolVersion: 1, pid: process.pid, token }) + "\n"));
21
+ socket.on("data", data => {
22
+ buffer += data;
23
+ if (Buffer.byteLength(buffer) > 65_536) { abort(new Error("Oversized DEV control frame")); socket.destroy(); return; }
24
+ while (buffer.includes("\n")) {
25
+ const index = buffer.indexOf("\n"), line = buffer.slice(0, index); buffer = buffer.slice(index + 1);
26
+ try {
27
+ const message = JSON.parse(line);
28
+ if (message.protocolVersion !== 1) throw new Error("Unsupported DEV protocol");
29
+ if (!accepted && message.type === "accepted") { accepted = true; clearTimeout(timer); accept(); }
30
+ else if (accepted && message.type === "stop") lifecycle.abort(new Error("Windows DEV stop requested"));
31
+ else throw new Error("Unexpected DEV control frame");
32
+ } catch (error) { abort(error); socket.destroy(); return; }
33
+ }
34
+ });
35
+ try { await handshake; }
36
+ catch (error) { socket.destroy(); throw error; }
37
+ finally { clearTimeout(timer); }
38
+ return {
39
+ async stopped() {
40
+ finishing = true;
41
+ if (socket.destroyed) return;
42
+ await new Promise(resolve => {
43
+ const timeout = setTimeout(() => { socket.destroy(); resolve(); }, 1_000);
44
+ socket.once("close", () => { clearTimeout(timeout); resolve(); });
45
+ socket.end(JSON.stringify({ type: "stopped", protocolVersion: 1 }) + "\n", () => { clearTimeout(timeout); socket.destroy(); resolve(); });
46
+ });
47
+ },
48
+ };
49
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "agent-template",
3
+ "version": "0.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "version": "0.0.0",
9
+ "dependencies": {
10
+ "typebox": "1.3.7"
11
+ },
12
+ "engines": {
13
+ "node": ">=24"
14
+ }
15
+ },
16
+ "node_modules/typebox": {
17
+ "version": "1.3.7",
18
+ "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.7.tgz",
19
+ "integrity": "sha512-meKuifc33Pccx0O6PdIzYMq3Og8zvP4TIi/a+Bw3AEMZMxOD0+RHGQvpglEe6Zdy3wZ8nqn/j95h8LUZLk/6Hg==",
20
+ "license": "MIT"
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "version": "0.0.0",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "start": "node --env-file-if-exists=.env start.mjs"
7
+ },
8
+ "engines": {
9
+ "node": ">=24"
10
+ },
11
+ "dependencies": {
12
+ "typebox": "1.3.7"
13
+ }
14
+ }
@@ -0,0 +1,11 @@
1
+ # 搭搭品牌资源
2
+
3
+ 这里是开发者平台、CLI 登录结果页、本地预览调试页和 Booklet 创作手册页共用的配色与 Logo 源文件。
4
+
5
+ - `tokens.css`:浅色与深色主题、文字、间距、圆角和状态色。
6
+ - `logo.png`:用户提供的原始品牌图,不修改图形。
7
+ - `login-result.css`:CLI 本机登录结果页的独立布局。
8
+
9
+ 此目录保留工程与 Creator 所需的品牌资源。开发者后台及调试网页的品牌源文件与构建校验由 developer-platform 维护,CLI 不再同步网页资源。
10
+
11
+ 按钮圆角 12px、内容面板 20px、品牌区域 28px。主要操作和选中态使用青蓝色,成功、提醒、失败使用语义状态色。手机模拟器内部保持 App 的呈现,外围调试工具遵循平台主题。
@@ -0,0 +1,25 @@
1
+ * { box-sizing: border-box; }
2
+ html { color-scheme: light dark; background: var(--color-paper); color: var(--color-ink); font-family: var(--font-body); }
3
+ body { min-width: 320px; min-height: 100dvh; margin: 0; }
4
+ .result-shell { width: min(640px, calc(100% - 40px)); margin: 0 auto; padding: clamp(28px, 7vh, 72px) 0 24px; }
5
+ .result-brand { display: flex; align-items: center; gap: 12px; color: var(--color-ink-strong); font-size: 18px; font-weight: 650; }
6
+ .result-brand img { display: block; flex: none; }
7
+ .result-card { margin-top: 40px; padding: clamp(24px, 5vw, 40px); border: 1px solid var(--color-rule); border-radius: var(--radius-xl); background: var(--color-paper-raised); box-shadow: var(--shadow-whisper); animation: arrive var(--dur-long) var(--ease-out) both; }
8
+ .result-heading { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 12px; }
9
+ .result-eyebrow { margin: 0; color: var(--color-accent); font-size: 13px; font-weight: 600; }
10
+ .result-status { border-radius: var(--radius-pill); padding: 6px 12px; background: var(--color-paper-soft); color: var(--color-ink-soft); font-size: 12px; }
11
+ .result-status[data-tone="success"] { color: var(--color-success); background: var(--color-success-soft); }
12
+ .result-status[data-tone="error"] { color: var(--color-danger); background: var(--color-danger-soft); }
13
+ h1 { margin: 24px 0 16px; color: var(--color-ink-strong); font-size: clamp(28px, 5vw, 40px); letter-spacing: -.035em; line-height: 1.3; }
14
+ p { text-wrap: pretty; overflow-wrap: anywhere; }
15
+ .result-message { margin: 0; color: var(--color-ink-soft); font-size: 15px; line-height: 1.8; }
16
+ .result-next { margin: 28px 0; padding: 24px; border-radius: var(--radius-lg); background: var(--color-brand-panel); }
17
+ .result-next strong { color: var(--color-ink-strong); font-size: 16px; }
18
+ .result-next p { margin: 10px 0 0; color: var(--color-ink-soft); font-size: 13px; line-height: 1.8; }
19
+ .result-next code { display: block; margin-top: 16px; color: var(--color-accent); font: 14px/1.6 var(--font-mono); }
20
+ .result-link { display: inline-flex; align-items: center; justify-content: center; min-height: var(--control-height); padding: 12px 20px; border-radius: var(--radius-md); background: var(--color-accent); color: var(--color-accent-ink); font-size: 14px; font-weight: 600; text-decoration: none; transition: background var(--dur-short) var(--ease-out); }
21
+ .result-link:hover { background: var(--color-accent-hover); }
22
+ :focus-visible { outline: 3px solid var(--color-focus); outline-offset: 4px; }
23
+ footer { margin-top: 28px; color: var(--color-muted); font-size: 12px; }
24
+ @keyframes arrive { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
25
+ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; } }
Binary file
@@ -0,0 +1,100 @@
1
+ /* Dada product palette: cool white, ink and the supplied cyan brand mark.
2
+ Controls: 12px; content panels: 20px; brand surfaces: 28px; status: pill. */
3
+ :root {
4
+ color-scheme: light;
5
+ --color-paper: #f4f8fc;
6
+ --color-paper-soft: #eaf1f7;
7
+ --color-paper-raised: #ffffff;
8
+ --color-ink: #203344;
9
+ --color-ink-strong: #102738;
10
+ --color-ink-soft: #506577;
11
+ --color-muted: #566d80;
12
+ --color-rule: #dce6ee;
13
+ --color-rule-strong: #b5c9d8;
14
+ --color-accent: #007399;
15
+ --color-accent-hover: #006889;
16
+ --color-accent-soft: #e0f5fd;
17
+ --color-accent-ink: #ffffff;
18
+ --color-brand-bright: #24c9f1;
19
+ --color-brand-panel: #e3f5fc;
20
+ --color-focus: #007399;
21
+ --color-success: #13704e;
22
+ --color-success-soft: #e5f5ed;
23
+ --color-warning: #85531b;
24
+ --color-warning-soft: #fff1da;
25
+ --color-danger: #b33845;
26
+ --color-danger-soft: #ffedf0;
27
+ --color-overlay: rgb(12 31 48 / 38%);
28
+ --color-shadow: rgb(27 74 106 / 8%);
29
+ --color-transparent: transparent;
30
+ --font-display: "SF Pro Display", "PingFang SC", "Microsoft YaHei", sans-serif;
31
+ --font-body: "SF Pro Text", "PingFang SC", "Microsoft YaHei", sans-serif;
32
+ --font-mono: "SFMono-Regular", Consolas, monospace;
33
+ --space-3xs: .125rem;
34
+ --space-2xs: .25rem;
35
+ --space-xs: .5rem;
36
+ --space-sm: .75rem;
37
+ --space-md: 1rem;
38
+ --space-lg: 1.5rem;
39
+ --space-xl: 2.5rem;
40
+ --space-2xl: 4rem;
41
+ --space-3xl: 6rem;
42
+ --space-4xl: 9rem;
43
+ --text-xs: .75rem;
44
+ --text-sm: .875rem;
45
+ --text-base: 1rem;
46
+ --text-md: 1.125rem;
47
+ --text-lg: 1.25rem;
48
+ --text-xl: 1.625rem;
49
+ --text-2xl: 2rem;
50
+ --text-display: clamp(2rem, 3.2vw, 2.75rem);
51
+ --ease-out: cubic-bezier(.16, 1, .3, 1);
52
+ --dur-micro: 100ms;
53
+ --dur-short: 180ms;
54
+ --dur-long: 320ms;
55
+ --rule-thin: 1px;
56
+ --radius-xs: .5rem;
57
+ --radius-sm: .75rem;
58
+ --radius-md: .75rem;
59
+ --radius-lg: 1.25rem;
60
+ --radius-xl: 1.75rem;
61
+ --radius-pill: 999px;
62
+ --control-height: 3rem;
63
+ --shadow-whisper: 0 4px 16px var(--color-shadow);
64
+ --shadow-sheet: 0 16px 48px var(--color-shadow);
65
+ --z-base: 1;
66
+ --z-raised: 10;
67
+ --z-dropdown: 100;
68
+ --z-sticky: 200;
69
+ --z-modal: 400;
70
+ --z-toast: 500;
71
+ --z-tooltip: 600;
72
+ }
73
+ @media (prefers-color-scheme: dark) {
74
+ :root {
75
+ color-scheme: dark;
76
+ --color-paper: #101922;
77
+ --color-paper-soft: #203240;
78
+ --color-paper-raised: #17242f;
79
+ --color-ink: #e3edf5;
80
+ --color-ink-strong: #f4faff;
81
+ --color-ink-soft: #b1c3d1;
82
+ --color-muted: #98afc0;
83
+ --color-rule: #2d4252;
84
+ --color-rule-strong: #4b677b;
85
+ --color-accent: #46cef0;
86
+ --color-accent-hover: #82e5ff;
87
+ --color-accent-soft: #143b4b;
88
+ --color-accent-ink: #092e3b;
89
+ --color-brand-panel: #14303f;
90
+ --color-focus: #46cef0;
91
+ --color-success: #7ae0b0;
92
+ --color-success-soft: #173b31;
93
+ --color-warning: #efc787;
94
+ --color-warning-soft: #3b3021;
95
+ --color-danger: #ff9aa5;
96
+ --color-danger-soft: #402a33;
97
+ --color-overlay: rgb(2 10 18 / 65%);
98
+ --color-shadow: rgb(0 8 18 / 25%);
99
+ }
100
+ }