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/README.md +183 -0
- package/dist/index.js +39901 -0
- package/package.json +45 -0
- package/scripts/mail-watch-agent.sh +194 -0
- package/scripts/postinstall.cjs +42 -0
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`);
|