@fieldwangai/agentflow 0.1.102 → 0.1.105

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,180 @@
1
+ import crypto from "crypto";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { getAgentflowDataRoot } from "./paths.mjs";
5
+
6
+ const REGISTRY_VERSION = 1;
7
+
8
+ function registryPath() {
9
+ return path.join(getAgentflowDataRoot(), "collaboration", "prd-workflows.json");
10
+ }
11
+
12
+ function readRegistry() {
13
+ try {
14
+ const filePath = registryPath();
15
+ if (!fs.existsSync(filePath)) return { version: REGISTRY_VERSION, workflows: {} };
16
+ const parsed = JSON.parse(fs.readFileSync(filePath, "utf-8"));
17
+ return {
18
+ version: REGISTRY_VERSION,
19
+ workflows: parsed?.workflows && typeof parsed.workflows === "object" && !Array.isArray(parsed.workflows)
20
+ ? parsed.workflows
21
+ : {},
22
+ };
23
+ } catch {
24
+ return { version: REGISTRY_VERSION, workflows: {} };
25
+ }
26
+ }
27
+
28
+ function writeRegistry(registry) {
29
+ const filePath = registryPath();
30
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
31
+ const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
32
+ fs.writeFileSync(tempPath, JSON.stringify({
33
+ version: REGISTRY_VERSION,
34
+ workflows: registry?.workflows || {},
35
+ }, null, 2) + "\n", "utf-8");
36
+ fs.renameSync(tempPath, filePath);
37
+ }
38
+
39
+ function normalizeUserId(value) {
40
+ return String(value || "").trim().toLowerCase();
41
+ }
42
+
43
+ function normalizeTapdId(value) {
44
+ return String(value || "").trim();
45
+ }
46
+
47
+ function publicWorkflow(record, userId = "") {
48
+ if (!record) return null;
49
+ const actorId = normalizeUserId(userId);
50
+ const members = record.members && typeof record.members === "object" ? record.members : {};
51
+ return {
52
+ id: record.id,
53
+ tapdId: record.tapdId,
54
+ ownerId: record.ownerId,
55
+ role: actorId === record.ownerId ? "owner" : String(members[actorId] || ""),
56
+ memberCount: Object.keys(members).length,
57
+ members: Object.entries(members).map(([id, role]) => ({ userId: id, role })),
58
+ createdAt: record.createdAt || "",
59
+ updatedAt: record.updatedAt || "",
60
+ };
61
+ }
62
+
63
+ export function prdWorkflowCollaborationAccess(record, userId) {
64
+ if (!record) return { allowed: true, role: "" };
65
+ const actorId = normalizeUserId(userId);
66
+ const role = actorId === record.ownerId ? "owner" : String(record.members?.[actorId] || "");
67
+ return {
68
+ allowed: role === "owner" || role === "editor" || role === "viewer",
69
+ writable: role === "owner" || role === "editor",
70
+ role,
71
+ };
72
+ }
73
+
74
+ export function getPrdWorkflowCollaborationById(workflowId) {
75
+ return readRegistry().workflows[String(workflowId || "").trim()] || null;
76
+ }
77
+
78
+ export function getPrdWorkflowCollaborationForUser(tapdId, userId) {
79
+ const normalizedTapdId = normalizeTapdId(tapdId);
80
+ const actorId = normalizeUserId(userId);
81
+ if (!normalizedTapdId || !actorId) return null;
82
+ const records = Object.values(readRegistry().workflows)
83
+ .filter((record) => (
84
+ record?.tapdId === normalizedTapdId
85
+ && prdWorkflowCollaborationAccess(record, actorId).allowed
86
+ ))
87
+ .sort((left, right) => String(right?.updatedAt || "").localeCompare(String(left?.updatedAt || "")));
88
+ return records.find((record) => record.ownerId === actorId) || records[0] || null;
89
+ }
90
+
91
+ export function ensurePrdWorkflowCollaboration({ tapdId, userId }) {
92
+ const ownerId = normalizeUserId(userId);
93
+ const normalizedTapdId = normalizeTapdId(tapdId);
94
+ if (!ownerId) return { error: "Authentication required", status: 401 };
95
+ if (!normalizedTapdId) return { error: "Missing tapdId", status: 400 };
96
+ const registry = readRegistry();
97
+ let record = Object.values(registry.workflows).find((item) => (
98
+ item?.tapdId === normalizedTapdId && item?.ownerId === ownerId
99
+ )) || null;
100
+ if (record) return { record, workflow: publicWorkflow(record, ownerId), created: false };
101
+ const now = new Date().toISOString();
102
+ const workflowId = `prd_${crypto.randomBytes(12).toString("hex")}`;
103
+ record = {
104
+ id: workflowId,
105
+ tapdId: normalizedTapdId,
106
+ ownerId,
107
+ members: { [ownerId]: "owner" },
108
+ createdAt: now,
109
+ updatedAt: now,
110
+ };
111
+ registry.workflows[workflowId] = record;
112
+ writeRegistry(registry);
113
+ return { record, workflow: publicWorkflow(record, ownerId), created: true };
114
+ }
115
+
116
+ export function prdWorkflowCollaborationSummary(record, userId) {
117
+ return publicWorkflow(record, userId);
118
+ }
119
+
120
+ export function addPrdWorkflowCollaborationMember({
121
+ workflowId,
122
+ userId,
123
+ memberUserId,
124
+ role = "editor",
125
+ }) {
126
+ const registry = readRegistry();
127
+ const record = registry.workflows[String(workflowId || "").trim()];
128
+ if (!record) return { error: "PRD Workflow collaboration not found", status: 404 };
129
+ const actorId = normalizeUserId(userId);
130
+ const targetId = normalizeUserId(memberUserId);
131
+ if (prdWorkflowCollaborationAccess(record, actorId).role !== "owner") {
132
+ return { error: "Only the Workflow owner can add members", status: 403 };
133
+ }
134
+ if (!targetId) return { error: "Missing member user", status: 400 };
135
+ if (targetId === record.ownerId) {
136
+ return { workflow: publicWorkflow(record, actorId), unchanged: true };
137
+ }
138
+ const conflicting = Object.values(registry.workflows).find((item) => (
139
+ item?.id !== record.id
140
+ && item?.tapdId === record.tapdId
141
+ && prdWorkflowCollaborationAccess(item, targetId).allowed
142
+ ));
143
+ if (conflicting) {
144
+ return { error: "该用户已经加入同一 TAPD ID 的另一个 Workflow 分享", status: 409 };
145
+ }
146
+ record.members = record.members && typeof record.members === "object" ? record.members : {};
147
+ record.members[targetId] = role === "viewer" ? "viewer" : "editor";
148
+ record.updatedAt = new Date().toISOString();
149
+ writeRegistry(registry);
150
+ return { workflow: publicWorkflow(record, actorId), memberUserId: targetId };
151
+ }
152
+
153
+ export function removePrdWorkflowCollaborationMember({
154
+ workflowId,
155
+ userId,
156
+ memberUserId,
157
+ }) {
158
+ const registry = readRegistry();
159
+ const record = registry.workflows[String(workflowId || "").trim()];
160
+ if (!record) return { error: "PRD Workflow collaboration not found", status: 404 };
161
+ const actorId = normalizeUserId(userId);
162
+ const targetId = normalizeUserId(memberUserId || actorId);
163
+ const access = prdWorkflowCollaborationAccess(record, actorId);
164
+ if (!access.allowed) return { error: "PRD Workflow collaboration permission denied", status: 403 };
165
+ if (targetId === record.ownerId) return { error: "Workflow owner cannot leave", status: 400 };
166
+ if (access.role !== "owner" && targetId !== actorId) {
167
+ return { error: "Only the Workflow owner can remove another member", status: 403 };
168
+ }
169
+ if (!record.members?.[targetId]) {
170
+ return { workflow: publicWorkflow(record, actorId), unchanged: true };
171
+ }
172
+ delete record.members[targetId];
173
+ record.updatedAt = new Date().toISOString();
174
+ writeRegistry(registry);
175
+ return {
176
+ workflow: publicWorkflow(record, actorId),
177
+ removedUserId: targetId,
178
+ left: targetId === actorId,
179
+ };
180
+ }