dreamfactory 0.1.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/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "dreamfactory",
3
+ "version": "0.1.0",
4
+ "description": "DreamFactory — AI-powered short drama generator for digital characters",
5
+ "type": "module",
6
+ "bin": {
7
+ "dreamfactory": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "scripts"
12
+ ],
13
+ "scripts": {
14
+ "build": "pnpm -r build",
15
+ "build:bundle": "tsup",
16
+ "prepack": "tsup",
17
+ "postinstall": "node scripts/postinstall.cjs",
18
+ "dev": "pnpm --filter @dreamfactory/cli dev",
19
+ "lint": "pnpm -r lint",
20
+ "clean": "pnpm -r clean",
21
+ "test:core": "pnpm --filter @dreamfactory/core build && pnpm --filter @dreamfactory/core test",
22
+ "test:core:video-live": "pnpm --filter @dreamfactory/core build && RUN_OPENROUTER_VIDEO_INTEGRATION=1 pnpm --filter @dreamfactory/core test"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "engines": {
28
+ "node": ">=20"
29
+ },
30
+ "pnpm": {
31
+ "onlyBuiltDependencies": ["esbuild"]
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^22.0.0",
35
+ "@types/react": "^18.3.0",
36
+ "dotenv": "^16.4.7",
37
+ "ink": "^5.1.0",
38
+ "ink-select-input": "^6.0.0",
39
+ "ink-spinner": "^5.0.0",
40
+ "ink-text-input": "^6.0.0",
41
+ "react": "^18.3.1",
42
+ "tsup": "^8.5.1",
43
+ "typescript": "^5.7.0"
44
+ }
45
+ }
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env bash
2
+ # 定时检测 Mail Broker 收件箱;若有未读则调用 Cursor Agent CLI 唤醒 agent。
3
+ #
4
+ # 依赖: curl、python3;PATH 上需有 Cursor Agent CLI(通常为 `agent`,可通过环境变量指定)。
5
+ #
6
+ # 环境变量(均有与 AGENT.md 一致的默认值):
7
+ # BROKER_URL 默认 http://127.0.0.1:9800
8
+ # INBOX_ADDRESS 默认 linkyun-dream-factory-coder@local
9
+ # AGENT_ID 默认 569e1858-b8ba-49a6-b458-ccbef6884e94
10
+ # WORKSPACE 默认本脚本所在仓库根目录(dream-factory)
11
+ # POLL_INTERVAL_SEC 轮询间隔秒数,默认 60
12
+ # CURSOR_AGENT_BIN 可执行文件;默认依次尝试已设置值、agent、cursor
13
+ # AGENT_EXTRA_ARGS 追加传给 agent 的参数(谨慎包含空格,需自行引用)
14
+ # AGENT_FORCE 若为 1,则传入 --force
15
+ # LOCK_DIR 并发锁目录,避免上一轮 agent 未结束时再次启动
16
+ #
17
+ # 说明:
18
+ # Cursor Agent CLI 在终端中可能仍从 stdin 读入;若不在子进程中关闭 stdin,任务结束后进程
19
+ # 可能一直等待输入,导致 mail-watch 无法释放锁并进入下一轮轮询。run_agent 已对子进程使用
20
+ # 「</dev/null」避免挂起。若仍异常,请确认未对 agent 传入 --resume/--continue,必要时用
21
+ # timeout(1) 作为兜底。
22
+ #
23
+ # 用法:
24
+ # ./scripts/mail-watch-agent.sh
25
+
26
+ set -euo pipefail
27
+
28
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
29
+ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
30
+
31
+ BROKER_URL="${BROKER_URL:-http://127.0.0.1:9800}"
32
+ INBOX_ADDRESS="${INBOX_ADDRESS:-linkyun-dream-factory-coder@local}"
33
+ AGENT_ID="${AGENT_ID:-569e1858-b8ba-49a6-b458-ccbef6884e94}"
34
+ WORKSPACE="${WORKSPACE:-$REPO_ROOT}"
35
+ POLL_INTERVAL_SEC="${POLL_INTERVAL_SEC:-60}"
36
+ LOCK_DIR="${LOCK_DIR:-/tmp/dream-factory-mail-watch-agent.lock}"
37
+
38
+ INBOX_URL="${BROKER_URL}/messages/inbox/${INBOX_ADDRESS}?agent_id=${AGENT_ID}"
39
+
40
+ PROMPT='你有新的邮件,根据AGENT.md的设定获取邮件,设置为已读,执行任务,回复或者转发邮件。如果需要完整的上下文信息,可以读取整个thread'
41
+
42
+ ts() { date '+%Y-%m-%dT%H:%M:%S%z'; }
43
+
44
+ resolve_agent_bin() {
45
+ if [[ -n "${CURSOR_AGENT_BIN:-}" && -x "${CURSOR_AGENT_BIN}" ]]; then
46
+ echo "$CURSOR_AGENT_BIN"
47
+ return 0
48
+ fi
49
+ if command -v agent >/dev/null 2>&1; then
50
+ command -v agent
51
+ return 0
52
+ fi
53
+ if command -v cursor >/dev/null 2>&1; then
54
+ command -v cursor
55
+ return 0
56
+ fi
57
+ echo "错误: 未找到 Cursor Agent CLI。请将 agent 加入 PATH,或设置 CURSOR_AGENT_BIN。" >&2
58
+ return 1
59
+ }
60
+
61
+ # stdin: JSON body。未读 -> 退出 0;无未读 -> 1;解析失败 -> 2
62
+ has_unread_python() {
63
+ python3 -c '
64
+ import json, sys
65
+ try:
66
+ raw = sys.stdin.read()
67
+ if not raw.strip():
68
+ sys.exit(1)
69
+ d = json.loads(raw)
70
+ except Exception:
71
+ sys.exit(2)
72
+
73
+ def truthy_unread_count(obj):
74
+ if not isinstance(obj, dict):
75
+ return False
76
+ for k in ("unread_count", "unread", "unread_total"):
77
+ if k in obj:
78
+ try:
79
+ return int(obj[k]) > 0
80
+ except (TypeError, ValueError):
81
+ pass
82
+ return False
83
+
84
+ if truthy_unread_count(d):
85
+ sys.exit(0)
86
+
87
+ msgs = []
88
+ if isinstance(d, list):
89
+ msgs = d
90
+ elif isinstance(d, dict):
91
+ msgs = d.get("messages") or d.get("data") or d.get("items") or []
92
+
93
+ if not msgs:
94
+ sys.exit(1)
95
+
96
+ def is_unread(m):
97
+ if not isinstance(m, dict):
98
+ return False
99
+ if m.get("read") is False or m.get("is_read") is False:
100
+ return True
101
+ return False
102
+
103
+ if any(is_unread(m) for m in msgs):
104
+ sys.exit(0)
105
+
106
+ read_keys = [m.get("read") for m in msgs if isinstance(m, dict) and "read" in m]
107
+ if read_keys and all(v is True for v in read_keys):
108
+ sys.exit(1)
109
+
110
+ if msgs and not read_keys:
111
+ sys.exit(0)
112
+
113
+ sys.exit(1)
114
+ '
115
+ }
116
+
117
+ fetch_inbox() {
118
+ curl -sfS --max-time 15 --connect-timeout 5 \
119
+ -H "Accept: application/json" \
120
+ "$INBOX_URL"
121
+ }
122
+
123
+ # 0: 有未读;1: 无未读;2: 请求/解析失败
124
+ has_unread() {
125
+ local body
126
+ if ! body="$(fetch_inbox)"; then
127
+ return 2
128
+ fi
129
+ if printf '%s' "$body" | has_unread_python; then
130
+ return 0
131
+ fi
132
+ return 1
133
+ }
134
+
135
+ lock_acquire() {
136
+ mkdir "$LOCK_DIR" 2>/dev/null
137
+ }
138
+
139
+ lock_release() {
140
+ rmdir "$LOCK_DIR" 2>/dev/null || true
141
+ }
142
+
143
+ run_agent() {
144
+ local bin
145
+ bin="$(resolve_agent_bin)" || return 1
146
+ local -a args=(--print --trust --workspace "$WORKSPACE")
147
+ if [[ "${AGENT_FORCE:-0}" == "1" ]]; then
148
+ args+=(--force)
149
+ fi
150
+ if [[ -n "${AGENT_EXTRA_ARGS:-}" ]]; then
151
+ # shellcheck disable=SC2206
152
+ args+=($AGENT_EXTRA_ARGS)
153
+ fi
154
+ args+=("$PROMPT")
155
+
156
+ echo "[mail-watch-agent] $(ts) 启动 Cursor Agent: $bin --print --trust --workspace ... (提示词已省略)"
157
+ # 关闭 stdin,避免 agent 在任务结束后仍等待终端输入而无法退出、阻塞外层轮询。
158
+ "$bin" "${args[@]}" </dev/null
159
+ }
160
+
161
+ tick() {
162
+ local st=0
163
+ has_unread || st=$?
164
+
165
+ if [[ "$st" -eq 2 ]]; then
166
+ echo "[mail-watch-agent] $(ts) 无法拉取收件箱(Broker 未启动或网络异常),${POLL_INTERVAL_SEC}s 后重试" >&2
167
+ return 0
168
+ fi
169
+
170
+ if [[ "$st" -ne 0 ]]; then
171
+ return 0
172
+ fi
173
+
174
+ if ! lock_acquire; then
175
+ echo "[mail-watch-agent] $(ts) 锁占用中(上一趟 agent 可能仍在执行),跳过" >&2
176
+ return 0
177
+ fi
178
+
179
+ local ok=0
180
+ run_agent || ok=$?
181
+ lock_release
182
+ if [[ "$ok" -ne 0 ]]; then
183
+ echo "[mail-watch-agent] $(ts) Agent 退出码: $ok" >&2
184
+ fi
185
+ }
186
+
187
+ echo "[mail-watch-agent] 仓库: $WORKSPACE"
188
+ echo "[mail-watch-agent] 收件箱: $INBOX_URL"
189
+ echo "[mail-watch-agent] 间隔: ${POLL_INTERVAL_SEC}s"
190
+
191
+ while true; do
192
+ tick || true
193
+ sleep "$POLL_INTERVAL_SEC"
194
+ done
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ const { existsSync, mkdirSync, writeFileSync } = require("node:fs");
3
+ const { join } = require("node:path");
4
+ const { homedir } = require("node:os");
5
+
6
+ const GLOBAL_DIR = join(homedir(), ".dreamfactory");
7
+ const GLOBAL_ENV = join(GLOBAL_DIR, ".env");
8
+
9
+ if (existsSync(GLOBAL_ENV)) {
10
+ process.exit(0);
11
+ }
12
+
13
+ mkdirSync(GLOBAL_DIR, { recursive: true });
14
+
15
+ const content = `# DreamFactory — global configuration (~/.dreamfactory/.env)
16
+ # A local .env in your project directory overrides keys below (see .env.example in repo).
17
+
18
+ # --- linkyun.co ---
19
+ LINKYUN_API_BASE=https://linkyun.co
20
+
21
+ # Session keys are normally written by CLI login to your project .env, not here.
22
+ # Uncomment only if you want the same linkyun session in every workspace:
23
+ # LINKYUN_API_KEY=
24
+ # LINKYUN_WORKSPACE_CODE=
25
+ # LINKYUN_USERNAME=
26
+
27
+ # --- LLM (OpenAI-compatible chat; defaults target OpenRouter) ---
28
+ # https://openrouter.ai/keys — or any gateway with /v1/chat/completions
29
+ LLM_BASE_URL=https://openrouter.ai/api/v1
30
+ LLM_MODEL=anthropic/claude-sonnet-4
31
+ LLM_API_KEY=
32
+
33
+ # Seedance (video)
34
+ SEEDANCE_API_KEY=
35
+
36
+ # Wan (storyboard images)
37
+ WAN_API_KEY=
38
+ `;
39
+
40
+ writeFileSync(GLOBAL_ENV, content, "utf-8");
41
+ console.log(`\n DreamFactory: Created global config at ${GLOBAL_ENV}`);
42
+ console.log(` Run "dreamfactory init" to configure API keys.\n`);