@workflow-code/cli 0.2.4-20261012003.codex-cli-web → 0.2.4-20261012005.codex-studio-integration
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/dist/{chunk-WN7VAGK6.js → chunk-5DWX3LL6.js} +1 -1
- package/dist/{chunk-MNY562W5.js → chunk-5FPIZZE4.js} +12 -7
- package/dist/{chunk-PGM3IPWA.js → chunk-SGVXPYCT.js} +1 -0
- package/dist/index.js +4 -4
- package/dist/{local-QLYFH4WT.js → local-XHR2O2FP.js} +2 -2
- package/dist/{mcp-LQ2IV6VA.js → mcp-RPT64F7T.js} +2 -2
- package/dist/web/kanban-workspace.js +10 -2
- package/dist/web/related-projects.mjs +528 -0
- package/dist/web/worker.mjs +7 -0
- package/dist/web/workflow-ui.js +10 -2
- package/dist/web/workflow-workspace.js +10 -2
- package/dist/{web-YUMRIE22.js → web-6SWHMU2A.js} +51 -13
- package/dist/{workspace-V3UXVFK6.js → workspace-OKFR57T6.js} +1 -1
- package/package.json +2 -2
|
@@ -12,7 +12,7 @@ import os from "os";
|
|
|
12
12
|
import path from "path";
|
|
13
13
|
import { readFileSync, realpathSync, statSync } from "fs";
|
|
14
14
|
import * as workflowCode from "workflow-code";
|
|
15
|
-
var
|
|
15
|
+
var defaultStorageApi = workflowCode;
|
|
16
16
|
function parseCliRelatedProjectSpec(value) {
|
|
17
17
|
const separatorIndex = value.indexOf("=");
|
|
18
18
|
if (separatorIndex <= 0 || separatorIndex === value.length - 1) {
|
|
@@ -30,6 +30,7 @@ function parseCliRelatedProjectSpec(value) {
|
|
|
30
30
|
}
|
|
31
31
|
function createCliRelatedProjectRuntime(options) {
|
|
32
32
|
if (options.relatedProjects.length === 0) return {};
|
|
33
|
+
const targetStorageApi = options.storageApi ?? defaultStorageApi;
|
|
33
34
|
const consumer = readProjectSnapshot(options.consumerProjectDir, "Current project");
|
|
34
35
|
const configuredByAlias = /* @__PURE__ */ new Map();
|
|
35
36
|
const configuredProjectIds = /* @__PURE__ */ new Set();
|
|
@@ -76,7 +77,7 @@ function createCliRelatedProjectRuntime(options) {
|
|
|
76
77
|
configured.projectDir,
|
|
77
78
|
`Related project "${alias}"`
|
|
78
79
|
);
|
|
79
|
-
if (currentTarget.projectId.toLowerCase() !== configured.projectId.toLowerCase()) {
|
|
80
|
+
if (currentConsumer.projectId.toLowerCase() !== consumer.projectId.toLowerCase() || currentTarget.projectId.toLowerCase() !== configured.projectId.toLowerCase()) {
|
|
80
81
|
return void 0;
|
|
81
82
|
}
|
|
82
83
|
const access = resolveWorkflowRelatedProjectAccess({
|
|
@@ -93,7 +94,8 @@ function createCliRelatedProjectRuntime(options) {
|
|
|
93
94
|
if (fixedStore === void 0 && typeof createRemoteStore !== "function") {
|
|
94
95
|
throw new Error("Installed workflow-code does not support related project storage.");
|
|
95
96
|
}
|
|
96
|
-
const
|
|
97
|
+
const roots = options.localStorageForProject?.(currentTarget.projectDir) ?? { kvStoreDir: localKvStoreDir, persistentValueStoreDir: localPersistentValueStoreDir };
|
|
98
|
+
const store = location === "local" && options.localStorageForProject ? targetStorageApi.createLocalWorkflowKVStore({ rootDir: roots.kvStoreDir, storageKind: "kv" }) : fixedStore ?? createRemoteStore({
|
|
97
99
|
consumerProjectId: currentConsumer.projectId,
|
|
98
100
|
targetProjectId: currentTarget.projectId,
|
|
99
101
|
alias,
|
|
@@ -105,8 +107,9 @@ function createCliRelatedProjectRuntime(options) {
|
|
|
105
107
|
runId: options.runId,
|
|
106
108
|
conversationId: options.conversationId
|
|
107
109
|
};
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
+
const persistentStore = location === "server" ? store : options.localStorageForProject && roots.persistentValueStoreDir !== void 0 ? targetStorageApi.createLocalWorkflowKVStore({ rootDir: roots.persistentValueStoreDir, storageKind: "persistent_value" }) : fixedPersistentValueStore;
|
|
111
|
+
const persistentValue = persistentStore === void 0 ? void 0 : targetStorageApi.createWorkflowPersistentValueContext?.({
|
|
112
|
+
store: persistentStore,
|
|
110
113
|
projectId: currentTarget.projectId,
|
|
111
114
|
source
|
|
112
115
|
});
|
|
@@ -119,8 +122,9 @@ function createCliRelatedProjectRuntime(options) {
|
|
|
119
122
|
// The roots settled above, not the raw options: the relation and the
|
|
120
123
|
// target's own run have to land on the same store, defaults
|
|
121
124
|
// included, or one side writes what the other cannot see.
|
|
122
|
-
kvStoreDir:
|
|
123
|
-
persistentValueStoreDir:
|
|
125
|
+
kvStoreDir: roots.kvStoreDir,
|
|
126
|
+
persistentValueStoreDir: roots.persistentValueStoreDir,
|
|
127
|
+
storageApi: targetStorageApi
|
|
124
128
|
});
|
|
125
129
|
return {
|
|
126
130
|
projectId: currentTarget.projectId,
|
|
@@ -139,6 +143,7 @@ function createCliRelatedProjectRuntime(options) {
|
|
|
139
143
|
}
|
|
140
144
|
function createLocalProjectRunner(options) {
|
|
141
145
|
const projectDir = options.projectDir;
|
|
146
|
+
const targetStorageApi = options.storageApi;
|
|
142
147
|
const inspect = targetStorageApi.inspectLocalWorkflowStructure;
|
|
143
148
|
const run = targetStorageApi.runLocalWorkflowWithReport;
|
|
144
149
|
const unwrap = targetStorageApi.readEntrypointOutput;
|
package/dist/index.js
CHANGED
|
@@ -24,16 +24,16 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
24
24
|
const prepared = prepareCliInvocation(argv);
|
|
25
25
|
argv = prepared.argv;
|
|
26
26
|
if (argv[0] === "web") {
|
|
27
|
-
const webCli = await import("./web-
|
|
27
|
+
const webCli = await import("./web-6SWHMU2A.js");
|
|
28
28
|
return webCli.main(argv.slice(1));
|
|
29
29
|
}
|
|
30
30
|
if (argv[0] === "workspace") {
|
|
31
|
-
const workspaceCli = await import("./workspace-
|
|
31
|
+
const workspaceCli = await import("./workspace-OKFR57T6.js");
|
|
32
32
|
await workspaceCli.main(argv.slice(1), { localeInitialized: true });
|
|
33
33
|
return typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
34
34
|
}
|
|
35
35
|
if (argv[0] === "mcp") {
|
|
36
|
-
const mcpCli = await import("./mcp-
|
|
36
|
+
const mcpCli = await import("./mcp-RPT64F7T.js");
|
|
37
37
|
return mcpCli.main(argv.slice(1));
|
|
38
38
|
}
|
|
39
39
|
if (argv.length === 0 || argv[0] === "help" || argv[0] === "--help" || argv[0] === "-h") {
|
|
@@ -43,7 +43,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
43
43
|
}
|
|
44
44
|
const { register } = await import("tsx/esm/api");
|
|
45
45
|
register();
|
|
46
|
-
const localCli = await import("./local-
|
|
46
|
+
const localCli = await import("./local-XHR2O2FP.js");
|
|
47
47
|
return localCli.main(argv, { localeInitialized: true });
|
|
48
48
|
}
|
|
49
49
|
function formatHelp() {
|
|
@@ -9,9 +9,9 @@ import {
|
|
|
9
9
|
main,
|
|
10
10
|
parseWorkflowArgs,
|
|
11
11
|
readInstalledWorkflowCoreVersion
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-5DWX3LL6.js";
|
|
13
13
|
import "./chunk-J2JMC4MZ.js";
|
|
14
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-5FPIZZE4.js";
|
|
15
15
|
import "./chunk-P6K3NB5S.js";
|
|
16
16
|
import "./chunk-7WEXRL5A.js";
|
|
17
17
|
import "./chunk-NSPRIPOP.js";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
parseCliRelatedProjectSpec
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-5FPIZZE4.js";
|
|
5
5
|
import {
|
|
6
6
|
isWorkflowAuthConfigLoggedIn,
|
|
7
7
|
normalizeWorkflowAuthConfig,
|
|
8
8
|
readCliAuthConfigSync,
|
|
9
9
|
resolveWorkflowDesktopConfigFilePath
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-SGVXPYCT.js";
|
|
11
11
|
import {
|
|
12
12
|
assertWorkflowUuid
|
|
13
13
|
} from "./chunk-P6K3NB5S.js";
|
|
@@ -12720,6 +12720,7 @@
|
|
|
12720
12720
|
Loop: "\u5FAA\u73AF",
|
|
12721
12721
|
"Loop end": "\u5FAA\u73AF\u7ED3\u675F",
|
|
12722
12722
|
"Managed by server": "\u7531\u670D\u52A1\u5668\u7BA1\u7406",
|
|
12723
|
+
"Manage this account through Studio.": "\u8BF7\u5728 Studio \u4E2D\u7BA1\u7406\u6B64\u8D26\u53F7\u3002",
|
|
12723
12724
|
Merge: "\u6C47\u5408",
|
|
12724
12725
|
Message: "\u6D88\u606F",
|
|
12725
12726
|
Monthly: "\u6BCF\u6708",
|
|
@@ -15144,8 +15145,9 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
15144
15145
|
|
|
15145
15146
|
// src/web/ui/account.ts
|
|
15146
15147
|
var AccountPanel = class {
|
|
15147
|
-
constructor(menu2, request2) {
|
|
15148
|
+
constructor(menu2, request2, openHostAccount) {
|
|
15148
15149
|
this.request = request2;
|
|
15150
|
+
this.openHostAccount = openHostAccount;
|
|
15149
15151
|
this.button.id = "web-account";
|
|
15150
15152
|
this.button.className = "web-account";
|
|
15151
15153
|
this.button.type = "button";
|
|
@@ -15217,6 +15219,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
15217
15219
|
});
|
|
15218
15220
|
}
|
|
15219
15221
|
request;
|
|
15222
|
+
openHostAccount;
|
|
15220
15223
|
button = document.createElement("button");
|
|
15221
15224
|
dialog = document.createElement("dialog");
|
|
15222
15225
|
state;
|
|
@@ -15226,6 +15229,10 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
15226
15229
|
return this.dialog.querySelector(selector);
|
|
15227
15230
|
}
|
|
15228
15231
|
open() {
|
|
15232
|
+
if (this.openHostAccount) {
|
|
15233
|
+
this.openHostAccount();
|
|
15234
|
+
return;
|
|
15235
|
+
}
|
|
15229
15236
|
this.render();
|
|
15230
15237
|
if (!this.dialog.open) {
|
|
15231
15238
|
this.dialog.showModal();
|
|
@@ -15805,7 +15812,8 @@ ${state2.user?.avatarUrl ?? ""}`;
|
|
|
15805
15812
|
};
|
|
15806
15813
|
let accountPanel;
|
|
15807
15814
|
queueMicrotask(() => {
|
|
15808
|
-
|
|
15815
|
+
const openHostAccount = document.body.dataset.accountHost === "true" ? () => window.parent.postMessage({ type: "workflow-account", version: 1 }, "*") : void 0;
|
|
15816
|
+
accountPanel = new AccountPanel(document.querySelector("#project-menu"), request2, openHostAccount);
|
|
15809
15817
|
});
|
|
15810
15818
|
return {
|
|
15811
15819
|
postMessage: async (message) => {
|
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
// src/web/related-projects.ts
|
|
2
|
+
import path3 from "node:path";
|
|
3
|
+
|
|
4
|
+
// src/related-projects.ts
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { readFileSync, realpathSync, statSync } from "node:fs";
|
|
8
|
+
import * as workflowCode from "workflow-code";
|
|
9
|
+
|
|
10
|
+
// .shared/project-info/index.ts
|
|
11
|
+
var WORKFLOW_PROJECT_DATA_STORAGE_MODES = ["local", "server", "both"];
|
|
12
|
+
var WORKFLOW_RELATED_PROJECT_ALIAS_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
13
|
+
var WORKFLOW_RELATED_PROJECT_LIMIT = 32;
|
|
14
|
+
var WORKFLOW_RELATED_PROJECT_PREFIX_LIMIT = 32;
|
|
15
|
+
var WORKFLOW_RELATED_PROJECT_KEY_LIMIT = 1024;
|
|
16
|
+
var WORKFLOW_PROJECT_UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
17
|
+
var PROJECT_INFO_ID_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
18
|
+
function createUndeclaredWorkflowProjectInfo() {
|
|
19
|
+
return {
|
|
20
|
+
localizedNames: {},
|
|
21
|
+
declarations: [],
|
|
22
|
+
platformSupport: { mode: "agnostic" },
|
|
23
|
+
dataStorage: null,
|
|
24
|
+
relatedProjects: []
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function normalizeWorkflowProjectInfo(value) {
|
|
28
|
+
if (!isRecord(value)) {
|
|
29
|
+
throw new Error("workflowCode.projectInfo must be an object.");
|
|
30
|
+
}
|
|
31
|
+
const localizedNames = normalizeLocalizedNames(value["localizedNames"]);
|
|
32
|
+
const declarations = normalizeIds(value["declarations"], "workflowCode.projectInfo.declarations");
|
|
33
|
+
const platformSupport = normalizePlatformSupport(value["platformSupport"]);
|
|
34
|
+
const dataStorage = normalizeDataStorage(value["dataStorage"]);
|
|
35
|
+
const relatedProjects = normalizeRelatedProjects(value["relatedProjects"]);
|
|
36
|
+
return { localizedNames, declarations, platformSupport, dataStorage, relatedProjects };
|
|
37
|
+
}
|
|
38
|
+
function readWorkflowProjectInfo(packageJson) {
|
|
39
|
+
if (!isRecord(packageJson)) return createUndeclaredWorkflowProjectInfo();
|
|
40
|
+
const workflowCode2 = packageJson["workflowCode"];
|
|
41
|
+
if (!isRecord(workflowCode2) || workflowCode2["projectInfo"] === void 0) {
|
|
42
|
+
return createUndeclaredWorkflowProjectInfo();
|
|
43
|
+
}
|
|
44
|
+
const projectInfo = normalizeWorkflowProjectInfo(workflowCode2["projectInfo"]);
|
|
45
|
+
const projectId = typeof packageJson["id"] === "string" ? packageJson["id"].trim() : void 0;
|
|
46
|
+
if (projectId !== void 0 && projectId !== "") {
|
|
47
|
+
assertWorkflowRelatedProjects(projectInfo, projectId);
|
|
48
|
+
}
|
|
49
|
+
return projectInfo;
|
|
50
|
+
}
|
|
51
|
+
function assertWorkflowRelatedProjects(projectInfo, currentProjectId) {
|
|
52
|
+
if (projectInfo.relatedProjects.length === 0) return;
|
|
53
|
+
const normalizedProjectId = currentProjectId.trim();
|
|
54
|
+
if (!WORKFLOW_PROJECT_UUID_PATTERN.test(normalizedProjectId)) {
|
|
55
|
+
throw new Error("package.json.id must be a UUID before related projects can be declared.");
|
|
56
|
+
}
|
|
57
|
+
if (projectInfo.relatedProjects.some(
|
|
58
|
+
(relation) => relation.projectId.toLowerCase() === normalizedProjectId.toLowerCase()
|
|
59
|
+
)) {
|
|
60
|
+
throw new Error("workflowCode.projectInfo.relatedProjects cannot reference the current project.");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function findWorkflowRelatedProject(projectInfo, alias) {
|
|
64
|
+
const normalizedAlias = alias.trim();
|
|
65
|
+
return projectInfo.relatedProjects.find((relation) => relation.alias === normalizedAlias);
|
|
66
|
+
}
|
|
67
|
+
function resolveWorkflowRelatedProjectAccess(input) {
|
|
68
|
+
const consumer = findWorkflowRelatedProject(input.consumerProjectInfo, input.alias);
|
|
69
|
+
if (consumer?.projectId.toLowerCase() !== input.targetProjectId.trim().toLowerCase()) {
|
|
70
|
+
return void 0;
|
|
71
|
+
}
|
|
72
|
+
const target = input.targetProjectInfo.relatedProjects.find(
|
|
73
|
+
(relation) => relation.projectId.toLowerCase() === input.consumerProjectId.trim().toLowerCase()
|
|
74
|
+
);
|
|
75
|
+
if (target === void 0) return void 0;
|
|
76
|
+
return { consumer, target, grant: target.grantToRelatedProject };
|
|
77
|
+
}
|
|
78
|
+
function isWorkflowProjectDataStorageLocationAllowed(projectInfo, location) {
|
|
79
|
+
const mode = projectInfo.dataStorage?.mode;
|
|
80
|
+
return mode === "both" || mode === location;
|
|
81
|
+
}
|
|
82
|
+
function normalizeLocalizedNames(value) {
|
|
83
|
+
if (value === void 0) return {};
|
|
84
|
+
if (!isRecord(value)) {
|
|
85
|
+
throw new Error("workflowCode.projectInfo.localizedNames must be an object.");
|
|
86
|
+
}
|
|
87
|
+
const entries = [];
|
|
88
|
+
for (const [rawLocale, rawName] of Object.entries(value)) {
|
|
89
|
+
const locale = rawLocale.trim();
|
|
90
|
+
if (locale === "") {
|
|
91
|
+
throw new Error("workflowCode.projectInfo.localizedNames contains an empty locale.");
|
|
92
|
+
}
|
|
93
|
+
if (typeof rawName !== "string") {
|
|
94
|
+
throw new Error(`workflowCode.projectInfo.localizedNames.${locale} must be a string.`);
|
|
95
|
+
}
|
|
96
|
+
const name = rawName.trim();
|
|
97
|
+
if (name !== "") entries.push([locale, name]);
|
|
98
|
+
}
|
|
99
|
+
return Object.fromEntries(entries);
|
|
100
|
+
}
|
|
101
|
+
function normalizePlatformSupport(value) {
|
|
102
|
+
if (value === void 0) return { mode: "agnostic" };
|
|
103
|
+
if (!isRecord(value)) {
|
|
104
|
+
throw new Error("workflowCode.projectInfo.platformSupport must be an object.");
|
|
105
|
+
}
|
|
106
|
+
if (value["mode"] === "agnostic") {
|
|
107
|
+
if (value["platforms"] !== void 0) {
|
|
108
|
+
throw new Error("workflowCode.projectInfo.platformSupport.platforms is not allowed in agnostic mode.");
|
|
109
|
+
}
|
|
110
|
+
return { mode: "agnostic" };
|
|
111
|
+
}
|
|
112
|
+
if (value["mode"] !== "specific") {
|
|
113
|
+
throw new Error('workflowCode.projectInfo.platformSupport.mode must be "agnostic" or "specific".');
|
|
114
|
+
}
|
|
115
|
+
const platforms = normalizeIds(
|
|
116
|
+
value["platforms"],
|
|
117
|
+
"workflowCode.projectInfo.platformSupport.platforms"
|
|
118
|
+
);
|
|
119
|
+
if (platforms.length === 0) {
|
|
120
|
+
throw new Error("workflowCode.projectInfo.platformSupport.platforms must contain at least one platform.");
|
|
121
|
+
}
|
|
122
|
+
return { mode: "specific", platforms };
|
|
123
|
+
}
|
|
124
|
+
function normalizeDataStorage(value) {
|
|
125
|
+
if (value === void 0 || value === null) return null;
|
|
126
|
+
if (!isRecord(value)) {
|
|
127
|
+
throw new Error("workflowCode.projectInfo.dataStorage must be an object.");
|
|
128
|
+
}
|
|
129
|
+
const mode = value["mode"];
|
|
130
|
+
if (typeof mode !== "string" || !WORKFLOW_PROJECT_DATA_STORAGE_MODES.includes(
|
|
131
|
+
mode
|
|
132
|
+
)) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
'workflowCode.projectInfo.dataStorage.mode must be "local", "server", or "both".'
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
return { mode };
|
|
138
|
+
}
|
|
139
|
+
function normalizeRelatedProjects(value) {
|
|
140
|
+
if (value === void 0) return [];
|
|
141
|
+
if (!Array.isArray(value)) {
|
|
142
|
+
throw new Error("workflowCode.projectInfo.relatedProjects must be an array.");
|
|
143
|
+
}
|
|
144
|
+
if (value.length > WORKFLOW_RELATED_PROJECT_LIMIT) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
`workflowCode.projectInfo.relatedProjects supports at most ${WORKFLOW_RELATED_PROJECT_LIMIT} items.`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
150
|
+
const projectIds = /* @__PURE__ */ new Set();
|
|
151
|
+
return value.map((item, index) => {
|
|
152
|
+
const field = `workflowCode.projectInfo.relatedProjects[${index}]`;
|
|
153
|
+
if (!isRecord(item)) throw new Error(`${field} must be an object.`);
|
|
154
|
+
const supportedFields = /* @__PURE__ */ new Set(["alias", "projectId", "grantToRelatedProject"]);
|
|
155
|
+
if (Object.keys(item).some((key) => !supportedFields.has(key))) {
|
|
156
|
+
throw new Error(`${field} contains unsupported fields.`);
|
|
157
|
+
}
|
|
158
|
+
const alias = readRelatedProjectString(item["alias"], `${field}.alias`);
|
|
159
|
+
if (!WORKFLOW_RELATED_PROJECT_ALIAS_PATTERN.test(alias)) {
|
|
160
|
+
throw new Error(`${field}.alias is invalid.`);
|
|
161
|
+
}
|
|
162
|
+
if (aliases.has(alias)) {
|
|
163
|
+
throw new Error(`workflowCode.projectInfo.relatedProjects contains duplicate alias "${alias}".`);
|
|
164
|
+
}
|
|
165
|
+
aliases.add(alias);
|
|
166
|
+
const projectId = readRelatedProjectString(item["projectId"], `${field}.projectId`);
|
|
167
|
+
if (!WORKFLOW_PROJECT_UUID_PATTERN.test(projectId)) {
|
|
168
|
+
throw new Error(`${field}.projectId must be a UUID.`);
|
|
169
|
+
}
|
|
170
|
+
const normalizedProjectId = projectId.toLowerCase();
|
|
171
|
+
if (projectIds.has(normalizedProjectId)) {
|
|
172
|
+
throw new Error(`workflowCode.projectInfo.relatedProjects contains duplicate projectId "${projectId}".`);
|
|
173
|
+
}
|
|
174
|
+
projectIds.add(normalizedProjectId);
|
|
175
|
+
return {
|
|
176
|
+
alias,
|
|
177
|
+
projectId,
|
|
178
|
+
grantToRelatedProject: normalizeRelatedProjectGrant(
|
|
179
|
+
item["grantToRelatedProject"],
|
|
180
|
+
`${field}.grantToRelatedProject`
|
|
181
|
+
)
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function normalizeRelatedProjectGrant(value, field) {
|
|
186
|
+
if (!isRecord(value)) throw new Error(`${field} must be an object.`);
|
|
187
|
+
const supported = /* @__PURE__ */ new Set(["read", "write", "knowledge", "entrypoints"]);
|
|
188
|
+
if (Object.keys(value).some((key) => !supported.has(key))) {
|
|
189
|
+
throw new Error(`${field} contains unsupported fields.`);
|
|
190
|
+
}
|
|
191
|
+
const grant = {
|
|
192
|
+
read: normalizeProjectAccessRule(value["read"], `${field}.read`),
|
|
193
|
+
write: normalizeProjectAccessRule(value["write"], `${field}.write`)
|
|
194
|
+
};
|
|
195
|
+
const knowledge = value["knowledge"];
|
|
196
|
+
if (knowledge !== void 0) {
|
|
197
|
+
if (!isRecord(knowledge)) throw new Error(`${field}.knowledge must be an object.`);
|
|
198
|
+
if (Object.keys(knowledge).some((key) => key !== "read" && key !== "write")) {
|
|
199
|
+
throw new Error(`${field}.knowledge contains unsupported fields.`);
|
|
200
|
+
}
|
|
201
|
+
grant.knowledge = {
|
|
202
|
+
read: normalizeProjectAccessRule(knowledge["read"], `${field}.knowledge.read`),
|
|
203
|
+
write: normalizeProjectAccessRule(knowledge["write"], `${field}.knowledge.write`)
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const entrypoints = value["entrypoints"];
|
|
207
|
+
if (entrypoints !== void 0) {
|
|
208
|
+
grant.entrypoints = normalizeProjectAccessRule(entrypoints, `${field}.entrypoints`);
|
|
209
|
+
}
|
|
210
|
+
return grant;
|
|
211
|
+
}
|
|
212
|
+
function normalizeProjectAccessRule(value, field) {
|
|
213
|
+
if (!isRecord(value)) throw new Error(`${field} must be an object.`);
|
|
214
|
+
const mode = value["mode"];
|
|
215
|
+
if (mode === "none" || mode === "all") {
|
|
216
|
+
if (Object.keys(value).some((key) => key !== "mode")) {
|
|
217
|
+
throw new Error(`${field} contains unsupported fields.`);
|
|
218
|
+
}
|
|
219
|
+
return { mode };
|
|
220
|
+
}
|
|
221
|
+
if (mode !== "prefixes") {
|
|
222
|
+
throw new Error(`${field}.mode must be "none", "all", or "prefixes".`);
|
|
223
|
+
}
|
|
224
|
+
if (Object.keys(value).some((key) => key !== "mode" && key !== "prefixes")) {
|
|
225
|
+
throw new Error(`${field} contains unsupported fields.`);
|
|
226
|
+
}
|
|
227
|
+
const rawPrefixes = value["prefixes"];
|
|
228
|
+
if (!Array.isArray(rawPrefixes) || rawPrefixes.length === 0 || rawPrefixes.length > WORKFLOW_RELATED_PROJECT_PREFIX_LIMIT) {
|
|
229
|
+
throw new Error(
|
|
230
|
+
`${field}.prefixes must contain 1 to ${WORKFLOW_RELATED_PROJECT_PREFIX_LIMIT} items.`
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
const prefixes = rawPrefixes.map((prefix, index) => {
|
|
234
|
+
const normalized = readRelatedProjectString(prefix, `${field}.prefixes[${index}]`);
|
|
235
|
+
if (normalized.length > WORKFLOW_RELATED_PROJECT_KEY_LIMIT || normalized.includes("\0")) {
|
|
236
|
+
throw new Error(`${field}.prefixes[${index}] is invalid.`);
|
|
237
|
+
}
|
|
238
|
+
return normalized;
|
|
239
|
+
});
|
|
240
|
+
if (new Set(prefixes).size !== prefixes.length) {
|
|
241
|
+
throw new Error(`${field}.prefixes contains duplicates.`);
|
|
242
|
+
}
|
|
243
|
+
return { mode: "prefixes", prefixes };
|
|
244
|
+
}
|
|
245
|
+
function readRelatedProjectString(value, field) {
|
|
246
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
247
|
+
throw new Error(`${field} must be a non-empty string.`);
|
|
248
|
+
}
|
|
249
|
+
return value.trim();
|
|
250
|
+
}
|
|
251
|
+
function normalizeIds(value, field) {
|
|
252
|
+
if (value === void 0) return [];
|
|
253
|
+
if (!Array.isArray(value)) throw new Error(`${field} must be an array.`);
|
|
254
|
+
const values = value.map((item, index) => {
|
|
255
|
+
if (typeof item !== "string" || !PROJECT_INFO_ID_PATTERN.test(item.trim())) {
|
|
256
|
+
throw new Error(`${field}[${index}] must be a lowercase identifier.`);
|
|
257
|
+
}
|
|
258
|
+
return item.trim();
|
|
259
|
+
});
|
|
260
|
+
return uniqueStrings(values, true);
|
|
261
|
+
}
|
|
262
|
+
function uniqueStrings(values, caseSensitive) {
|
|
263
|
+
const seen = /* @__PURE__ */ new Set();
|
|
264
|
+
const result = [];
|
|
265
|
+
for (const rawValue of values) {
|
|
266
|
+
const value = rawValue.trim();
|
|
267
|
+
if (value === "") continue;
|
|
268
|
+
const key = caseSensitive ? value : value.toLocaleLowerCase();
|
|
269
|
+
if (seen.has(key)) continue;
|
|
270
|
+
seen.add(key);
|
|
271
|
+
result.push(value);
|
|
272
|
+
}
|
|
273
|
+
return result;
|
|
274
|
+
}
|
|
275
|
+
function isRecord(value) {
|
|
276
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// .shared/workflow-ids/index.ts
|
|
280
|
+
var WORKFLOW_UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
281
|
+
function isWorkflowUuid(value) {
|
|
282
|
+
return WORKFLOW_UUID_PATTERN.test(value.trim());
|
|
283
|
+
}
|
|
284
|
+
function assertWorkflowUuid(value, label = "workflow id") {
|
|
285
|
+
const normalized = value.trim();
|
|
286
|
+
if (!isWorkflowUuid(normalized)) {
|
|
287
|
+
throw new Error(`Invalid ${label} "${value}".`);
|
|
288
|
+
}
|
|
289
|
+
return normalized;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/related-projects.ts
|
|
293
|
+
var defaultStorageApi = workflowCode;
|
|
294
|
+
function createCliRelatedProjectRuntime(options) {
|
|
295
|
+
if (options.relatedProjects.length === 0) return {};
|
|
296
|
+
const targetStorageApi = options.storageApi ?? defaultStorageApi;
|
|
297
|
+
const consumer = readProjectSnapshot(options.consumerProjectDir, "Current project");
|
|
298
|
+
const configuredByAlias = /* @__PURE__ */ new Map();
|
|
299
|
+
const configuredProjectIds = /* @__PURE__ */ new Set();
|
|
300
|
+
const configuredPaths = /* @__PURE__ */ new Set();
|
|
301
|
+
for (const spec of options.relatedProjects) {
|
|
302
|
+
if (configuredByAlias.has(spec.alias)) {
|
|
303
|
+
throw new Error(`Related project alias "${spec.alias}" was specified more than once.`);
|
|
304
|
+
}
|
|
305
|
+
const target = readProjectSnapshot(spec.projectDir, `Related project "${spec.alias}"`);
|
|
306
|
+
const targetProjectId = target.projectId.toLowerCase();
|
|
307
|
+
if (configuredProjectIds.has(targetProjectId)) {
|
|
308
|
+
throw new Error(`Related project UUID "${target.projectId}" was specified more than once.`);
|
|
309
|
+
}
|
|
310
|
+
if (configuredPaths.has(target.projectDir)) {
|
|
311
|
+
throw new Error(`Related project directory "${target.projectDir}" was specified more than once.`);
|
|
312
|
+
}
|
|
313
|
+
assertConfirmedRelationship(consumer, target, spec.alias);
|
|
314
|
+
configuredByAlias.set(spec.alias, target);
|
|
315
|
+
configuredProjectIds.add(targetProjectId);
|
|
316
|
+
configuredPaths.add(target.projectDir);
|
|
317
|
+
}
|
|
318
|
+
const localKvStoreDir = options.localKvStoreDir ?? path.join(os.tmpdir(), "workflow-code-kv");
|
|
319
|
+
const localPersistentValueStoreDir = options.localPersistentValueStoreDir;
|
|
320
|
+
const localKvStore = targetStorageApi.createLocalWorkflowKVStore({
|
|
321
|
+
rootDir: localKvStoreDir,
|
|
322
|
+
storageKind: "kv"
|
|
323
|
+
});
|
|
324
|
+
const localPersistentValueStore = typeof targetStorageApi.createWorkflowPersistentValueContext === "function" && localPersistentValueStoreDir !== void 0 ? targetStorageApi.createLocalWorkflowKVStore({
|
|
325
|
+
rootDir: localPersistentValueStoreDir,
|
|
326
|
+
storageKind: "persistent_value"
|
|
327
|
+
}) : void 0;
|
|
328
|
+
return {
|
|
329
|
+
localKvStore,
|
|
330
|
+
localResolver: createResolver("local", localKvStore, localPersistentValueStore),
|
|
331
|
+
serverResolver: createResolver("server")
|
|
332
|
+
};
|
|
333
|
+
function createResolver(location, fixedStore, fixedPersistentValueStore) {
|
|
334
|
+
return {
|
|
335
|
+
resolve(alias) {
|
|
336
|
+
const configured = configuredByAlias.get(alias);
|
|
337
|
+
if (configured === void 0) return void 0;
|
|
338
|
+
const currentConsumer = readProjectSnapshot(consumer.projectDir, "Current project");
|
|
339
|
+
const currentTarget = readProjectSnapshot(
|
|
340
|
+
configured.projectDir,
|
|
341
|
+
`Related project "${alias}"`
|
|
342
|
+
);
|
|
343
|
+
if (currentConsumer.projectId.toLowerCase() !== consumer.projectId.toLowerCase() || currentTarget.projectId.toLowerCase() !== configured.projectId.toLowerCase()) {
|
|
344
|
+
return void 0;
|
|
345
|
+
}
|
|
346
|
+
const access = resolveWorkflowRelatedProjectAccess({
|
|
347
|
+
consumerProjectId: currentConsumer.projectId,
|
|
348
|
+
consumerProjectInfo: currentConsumer.projectInfo,
|
|
349
|
+
targetProjectId: currentTarget.projectId,
|
|
350
|
+
targetProjectInfo: currentTarget.projectInfo,
|
|
351
|
+
alias
|
|
352
|
+
});
|
|
353
|
+
if (access === void 0 || !isWorkflowProjectDataStorageLocationAllowed(currentTarget.projectInfo, location)) {
|
|
354
|
+
return void 0;
|
|
355
|
+
}
|
|
356
|
+
const createRemoteStore = targetStorageApi.createRemoteWorkflowRelatedProjectKVStoreFromEnv;
|
|
357
|
+
if (fixedStore === void 0 && typeof createRemoteStore !== "function") {
|
|
358
|
+
throw new Error("Installed workflow-code does not support related project storage.");
|
|
359
|
+
}
|
|
360
|
+
const roots = options.localStorageForProject?.(currentTarget.projectDir) ?? { kvStoreDir: localKvStoreDir, persistentValueStoreDir: localPersistentValueStoreDir };
|
|
361
|
+
const store = location === "local" && options.localStorageForProject ? targetStorageApi.createLocalWorkflowKVStore({ rootDir: roots.kvStoreDir, storageKind: "kv" }) : fixedStore ?? createRemoteStore({
|
|
362
|
+
consumerProjectId: currentConsumer.projectId,
|
|
363
|
+
targetProjectId: currentTarget.projectId,
|
|
364
|
+
alias,
|
|
365
|
+
env: options.env
|
|
366
|
+
});
|
|
367
|
+
if (store === void 0) return void 0;
|
|
368
|
+
const source = {
|
|
369
|
+
projectId: currentConsumer.projectId,
|
|
370
|
+
runId: options.runId,
|
|
371
|
+
conversationId: options.conversationId
|
|
372
|
+
};
|
|
373
|
+
const persistentStore = location === "server" ? store : options.localStorageForProject && roots.persistentValueStoreDir !== void 0 ? targetStorageApi.createLocalWorkflowKVStore({ rootDir: roots.persistentValueStoreDir, storageKind: "persistent_value" }) : fixedPersistentValueStore;
|
|
374
|
+
const persistentValue = persistentStore === void 0 ? void 0 : targetStorageApi.createWorkflowPersistentValueContext?.({
|
|
375
|
+
store: persistentStore,
|
|
376
|
+
projectId: currentTarget.projectId,
|
|
377
|
+
source
|
|
378
|
+
});
|
|
379
|
+
const runner = createLocalProjectRunner({
|
|
380
|
+
projectDir: currentTarget.projectDir,
|
|
381
|
+
// Where the target keeps its data decides which store its own run
|
|
382
|
+
// uses: a project that lives on the server runs against the server,
|
|
383
|
+
// exactly as it would if its author started it here.
|
|
384
|
+
location,
|
|
385
|
+
// The roots settled above, not the raw options: the relation and the
|
|
386
|
+
// target's own run have to land on the same store, defaults
|
|
387
|
+
// included, or one side writes what the other cannot see.
|
|
388
|
+
kvStoreDir: roots.kvStoreDir,
|
|
389
|
+
persistentValueStoreDir: roots.persistentValueStoreDir,
|
|
390
|
+
storageApi: targetStorageApi
|
|
391
|
+
});
|
|
392
|
+
return {
|
|
393
|
+
projectId: currentTarget.projectId,
|
|
394
|
+
grant: access.grant,
|
|
395
|
+
kv: targetStorageApi.createWorkflowKVContext({
|
|
396
|
+
store,
|
|
397
|
+
projectId: currentTarget.projectId,
|
|
398
|
+
source
|
|
399
|
+
}),
|
|
400
|
+
...persistentValue === void 0 ? {} : { persistentValue },
|
|
401
|
+
...runner === void 0 ? {} : { runner }
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
function createLocalProjectRunner(options) {
|
|
408
|
+
const projectDir = options.projectDir;
|
|
409
|
+
const targetStorageApi = options.storageApi;
|
|
410
|
+
const inspect = targetStorageApi.inspectLocalWorkflowStructure;
|
|
411
|
+
const run = targetStorageApi.runLocalWorkflowWithReport;
|
|
412
|
+
const unwrap = targetStorageApi.readEntrypointOutput;
|
|
413
|
+
if (typeof inspect !== "function" || typeof run !== "function" || typeof unwrap !== "function") {
|
|
414
|
+
return void 0;
|
|
415
|
+
}
|
|
416
|
+
return {
|
|
417
|
+
async listEntrypoints() {
|
|
418
|
+
const structure = inspect({ workflowDir: projectDir });
|
|
419
|
+
return (structure.entrypoints ?? []).map((entrypoint) => ({
|
|
420
|
+
id: entrypoint.id,
|
|
421
|
+
...entrypoint.title === void 0 ? {} : { title: entrypoint.title },
|
|
422
|
+
...entrypoint.description === void 0 ? {} : { description: entrypoint.description }
|
|
423
|
+
}));
|
|
424
|
+
},
|
|
425
|
+
async run(entrypointId, args) {
|
|
426
|
+
const report = await run({
|
|
427
|
+
workflowDir: projectDir,
|
|
428
|
+
entrypointId,
|
|
429
|
+
args: [...args],
|
|
430
|
+
storage: options.location === "server" ? { host: "server" } : {
|
|
431
|
+
host: "local",
|
|
432
|
+
local: {
|
|
433
|
+
kvStoreDir: options.kvStoreDir,
|
|
434
|
+
persistentValueStoreDir: options.persistentValueStoreDir
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
if (report.result === void 0) {
|
|
439
|
+
throw new Error(`Related project entrypoint "${entrypointId}" returned no result.`);
|
|
440
|
+
}
|
|
441
|
+
return unwrap(report.result.data);
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
function assertConfirmedRelationship(consumer, target, alias) {
|
|
446
|
+
const consumerRelation = consumer.projectInfo.relatedProjects.find(
|
|
447
|
+
(relation) => relation.alias === alias
|
|
448
|
+
);
|
|
449
|
+
if (consumerRelation?.projectId.toLowerCase() !== target.projectId.toLowerCase()) {
|
|
450
|
+
throw new Error(
|
|
451
|
+
`Current project does not declare related project "${alias}" with UUID "${target.projectId}".`
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
if (resolveWorkflowRelatedProjectAccess({
|
|
455
|
+
consumerProjectId: consumer.projectId,
|
|
456
|
+
consumerProjectInfo: consumer.projectInfo,
|
|
457
|
+
targetProjectId: target.projectId,
|
|
458
|
+
targetProjectInfo: target.projectInfo,
|
|
459
|
+
alias
|
|
460
|
+
}) === void 0) {
|
|
461
|
+
throw new Error(`Related project "${alias}" is not confirmed by both project manifests.`);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
function readProjectSnapshot(projectDir, label) {
|
|
465
|
+
let canonicalDir;
|
|
466
|
+
try {
|
|
467
|
+
canonicalDir = realpathSync(projectDir);
|
|
468
|
+
if (!statSync(canonicalDir).isDirectory()) {
|
|
469
|
+
throw new Error("not a directory");
|
|
470
|
+
}
|
|
471
|
+
} catch (error) {
|
|
472
|
+
throw new Error(`${label} directory is unavailable: ${projectDir}`, { cause: error });
|
|
473
|
+
}
|
|
474
|
+
const packagePath = path.join(canonicalDir, "package.json");
|
|
475
|
+
let packageJson;
|
|
476
|
+
try {
|
|
477
|
+
packageJson = JSON.parse(readFileSync(packagePath, "utf8"));
|
|
478
|
+
} catch (error) {
|
|
479
|
+
throw new Error(`${label} must contain a readable package.json: ${canonicalDir}`, {
|
|
480
|
+
cause: error
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
if (!isRecord2(packageJson) || typeof packageJson["id"] !== "string") {
|
|
484
|
+
throw new Error(`${label} package.json.id must be a UUID.`);
|
|
485
|
+
}
|
|
486
|
+
const projectId = assertWorkflowUuid(packageJson["id"], `${label} package.json.id`);
|
|
487
|
+
const projectInfo = readWorkflowProjectInfo(packageJson);
|
|
488
|
+
if (projectInfo.dataStorage === null) {
|
|
489
|
+
throw new Error(`${label} must declare workflowCode.projectInfo.dataStorage.mode.`);
|
|
490
|
+
}
|
|
491
|
+
return { projectDir: canonicalDir, projectId, projectInfo };
|
|
492
|
+
}
|
|
493
|
+
function isRecord2(value) {
|
|
494
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// src/web/kanban-data.ts
|
|
498
|
+
import { createHash } from "node:crypto";
|
|
499
|
+
import path2 from "node:path";
|
|
500
|
+
|
|
501
|
+
// .shared/kanban-project/index.ts
|
|
502
|
+
var KANBAN_FILE_MAX_BYTES = 50 * 1024 * 1024;
|
|
503
|
+
|
|
504
|
+
// src/web/kanban-data.ts
|
|
505
|
+
var webProjectDataDirectory = (storageDir, projectDir) => path2.join(storageDir, createHash("sha256").update(projectDir).digest("hex").slice(0, 24));
|
|
506
|
+
|
|
507
|
+
// src/web/related-projects.ts
|
|
508
|
+
function createWebRelatedProjectRuntime(core, options) {
|
|
509
|
+
const roots = (projectDir) => {
|
|
510
|
+
const directory = webProjectDataDirectory(options.storageDir, projectDir);
|
|
511
|
+
return { kvStoreDir: path3.join(directory, "kv"), persistentValueStoreDir: path3.join(directory, "values") };
|
|
512
|
+
};
|
|
513
|
+
const local = roots(options.projectDir);
|
|
514
|
+
return createCliRelatedProjectRuntime({
|
|
515
|
+
consumerProjectDir: options.projectDir,
|
|
516
|
+
relatedProjects: options.relatedProjects,
|
|
517
|
+
localKvStoreDir: local.kvStoreDir,
|
|
518
|
+
localPersistentValueStoreDir: local.persistentValueStoreDir,
|
|
519
|
+
localStorageForProject: roots,
|
|
520
|
+
storageApi: core,
|
|
521
|
+
runId: options.runId,
|
|
522
|
+
conversationId: options.conversationId,
|
|
523
|
+
env: process.env
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
export {
|
|
527
|
+
createWebRelatedProjectRuntime
|
|
528
|
+
};
|
package/dist/web/worker.mjs
CHANGED
|
@@ -123,6 +123,13 @@ try {
|
|
|
123
123
|
process.env.WORKFLOW_DEVELOPER_ASSISTANT_WORKSPACE_MANIFEST = manifestPath;
|
|
124
124
|
}
|
|
125
125
|
const storage = { host: 'local', local: { kvStoreDir: path.join(dataDir, 'kv'), persistentValueStoreDir: path.join(dataDir, 'values') } };
|
|
126
|
+
if (request.method === 'run' || request.method === 'resolve') {
|
|
127
|
+
const { createWebRelatedProjectRuntime } = await import(new URL(import.meta.url.includes('/src/web/') ? './related-projects.ts' : './related-projects.mjs', import.meta.url));
|
|
128
|
+
const related = createWebRelatedProjectRuntime(core, { projectDir, storageDir: request.storageDir,
|
|
129
|
+
relatedProjects: request.relatedProjects ?? [], runId: request.runId, conversationId: request.conversationId });
|
|
130
|
+
storage.local.relatedProjectResolver = related.localResolver;
|
|
131
|
+
storage.server = { relatedProjectResolver: related.serverResolver };
|
|
132
|
+
}
|
|
126
133
|
const filesDir = path.join(dataDir, 'files');
|
|
127
134
|
const createdFiles = [];
|
|
128
135
|
const managed = createServerWorkflowFileStore({ rootDir: filesDir, workflowName: request.project.id, conversationId: request.conversationId });
|
package/dist/web/workflow-ui.js
CHANGED
|
@@ -12720,6 +12720,7 @@
|
|
|
12720
12720
|
Loop: "\u5FAA\u73AF",
|
|
12721
12721
|
"Loop end": "\u5FAA\u73AF\u7ED3\u675F",
|
|
12722
12722
|
"Managed by server": "\u7531\u670D\u52A1\u5668\u7BA1\u7406",
|
|
12723
|
+
"Manage this account through Studio.": "\u8BF7\u5728 Studio \u4E2D\u7BA1\u7406\u6B64\u8D26\u53F7\u3002",
|
|
12723
12724
|
Merge: "\u6C47\u5408",
|
|
12724
12725
|
Message: "\u6D88\u606F",
|
|
12725
12726
|
Monthly: "\u6BCF\u6708",
|
|
@@ -16837,8 +16838,9 @@ ${JSON.stringify(output.details, null, 2)}` : "");
|
|
|
16837
16838
|
|
|
16838
16839
|
// src/web/ui/account.ts
|
|
16839
16840
|
var AccountPanel = class {
|
|
16840
|
-
constructor(menu, request) {
|
|
16841
|
+
constructor(menu, request, openHostAccount) {
|
|
16841
16842
|
this.request = request;
|
|
16843
|
+
this.openHostAccount = openHostAccount;
|
|
16842
16844
|
this.button.id = "web-account";
|
|
16843
16845
|
this.button.className = "web-account";
|
|
16844
16846
|
this.button.type = "button";
|
|
@@ -16910,6 +16912,7 @@ ${JSON.stringify(output.details, null, 2)}` : "");
|
|
|
16910
16912
|
});
|
|
16911
16913
|
}
|
|
16912
16914
|
request;
|
|
16915
|
+
openHostAccount;
|
|
16913
16916
|
button = document.createElement("button");
|
|
16914
16917
|
dialog = document.createElement("dialog");
|
|
16915
16918
|
state;
|
|
@@ -16919,6 +16922,10 @@ ${JSON.stringify(output.details, null, 2)}` : "");
|
|
|
16919
16922
|
return this.dialog.querySelector(selector);
|
|
16920
16923
|
}
|
|
16921
16924
|
open() {
|
|
16925
|
+
if (this.openHostAccount) {
|
|
16926
|
+
this.openHostAccount();
|
|
16927
|
+
return;
|
|
16928
|
+
}
|
|
16922
16929
|
this.render();
|
|
16923
16930
|
if (!this.dialog.open) {
|
|
16924
16931
|
this.dialog.showModal();
|
|
@@ -17498,7 +17505,8 @@ ${state.user?.avatarUrl ?? ""}`;
|
|
|
17498
17505
|
};
|
|
17499
17506
|
let accountPanel;
|
|
17500
17507
|
queueMicrotask(() => {
|
|
17501
|
-
|
|
17508
|
+
const openHostAccount = document.body.dataset.accountHost === "true" ? () => window.parent.postMessage({ type: "workflow-account", version: 1 }, "*") : void 0;
|
|
17509
|
+
accountPanel = new AccountPanel(document.querySelector("#project-menu"), request, openHostAccount);
|
|
17502
17510
|
});
|
|
17503
17511
|
return {
|
|
17504
17512
|
postMessage: async (message) => {
|
|
@@ -16249,6 +16249,7 @@
|
|
|
16249
16249
|
Loop: "\u5FAA\u73AF",
|
|
16250
16250
|
"Loop end": "\u5FAA\u73AF\u7ED3\u675F",
|
|
16251
16251
|
"Managed by server": "\u7531\u670D\u52A1\u5668\u7BA1\u7406",
|
|
16252
|
+
"Manage this account through Studio.": "\u8BF7\u5728 Studio \u4E2D\u7BA1\u7406\u6B64\u8D26\u53F7\u3002",
|
|
16252
16253
|
Merge: "\u6C47\u5408",
|
|
16253
16254
|
Message: "\u6D88\u606F",
|
|
16254
16255
|
Monthly: "\u6BCF\u6708",
|
|
@@ -18688,8 +18689,9 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
18688
18689
|
|
|
18689
18690
|
// src/web/ui/account.ts
|
|
18690
18691
|
var AccountPanel = class {
|
|
18691
|
-
constructor(menu2, request) {
|
|
18692
|
+
constructor(menu2, request, openHostAccount) {
|
|
18692
18693
|
this.request = request;
|
|
18694
|
+
this.openHostAccount = openHostAccount;
|
|
18693
18695
|
this.button.id = "web-account";
|
|
18694
18696
|
this.button.className = "web-account";
|
|
18695
18697
|
this.button.type = "button";
|
|
@@ -18761,6 +18763,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
18761
18763
|
});
|
|
18762
18764
|
}
|
|
18763
18765
|
request;
|
|
18766
|
+
openHostAccount;
|
|
18764
18767
|
button = document.createElement("button");
|
|
18765
18768
|
dialog = document.createElement("dialog");
|
|
18766
18769
|
state;
|
|
@@ -18770,6 +18773,10 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
18770
18773
|
return this.dialog.querySelector(selector);
|
|
18771
18774
|
}
|
|
18772
18775
|
open() {
|
|
18776
|
+
if (this.openHostAccount) {
|
|
18777
|
+
this.openHostAccount();
|
|
18778
|
+
return;
|
|
18779
|
+
}
|
|
18773
18780
|
this.render();
|
|
18774
18781
|
if (!this.dialog.open) {
|
|
18775
18782
|
this.dialog.showModal();
|
|
@@ -19349,7 +19356,8 @@ ${state2.user?.avatarUrl ?? ""}`;
|
|
|
19349
19356
|
};
|
|
19350
19357
|
let accountPanel;
|
|
19351
19358
|
queueMicrotask(() => {
|
|
19352
|
-
|
|
19359
|
+
const openHostAccount = document.body.dataset.accountHost === "true" ? () => window.parent.postMessage({ type: "workflow-account", version: 1 }, "*") : void 0;
|
|
19360
|
+
accountPanel = new AccountPanel(document.querySelector("#project-menu"), request, openHostAccount);
|
|
19353
19361
|
});
|
|
19354
19362
|
return {
|
|
19355
19363
|
postMessage: async (message) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
assertLocalCliCoreCompatible
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-5DWX3LL6.js";
|
|
5
5
|
import {
|
|
6
6
|
assertKanbanMethodJson,
|
|
7
7
|
consumeKanbanRelatedProjectRevisionStream,
|
|
@@ -21,12 +21,13 @@ import {
|
|
|
21
21
|
sanitizeKanbanValues
|
|
22
22
|
} from "./chunk-HLMW6LAQ.js";
|
|
23
23
|
import "./chunk-J2JMC4MZ.js";
|
|
24
|
-
import "./chunk-
|
|
24
|
+
import "./chunk-5FPIZZE4.js";
|
|
25
25
|
import {
|
|
26
26
|
clearCliAuthConfig,
|
|
27
|
+
createEmptyWorkflowAuthConfig,
|
|
27
28
|
readCliAuthConfig,
|
|
28
29
|
writeCliAuthConfig
|
|
29
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-SGVXPYCT.js";
|
|
30
31
|
import {
|
|
31
32
|
isWorkflowProjectDataStorageLocationAllowed,
|
|
32
33
|
readWorkflowProjectInfo,
|
|
@@ -176,6 +177,7 @@ var messages_zh_cn_default = {
|
|
|
176
177
|
Loop: "\u5FAA\u73AF",
|
|
177
178
|
"Loop end": "\u5FAA\u73AF\u7ED3\u675F",
|
|
178
179
|
"Managed by server": "\u7531\u670D\u52A1\u5668\u7BA1\u7406",
|
|
180
|
+
"Manage this account through Studio.": "\u8BF7\u5728 Studio \u4E2D\u7BA1\u7406\u6B64\u8D26\u53F7\u3002",
|
|
179
181
|
Merge: "\u6C47\u5408",
|
|
180
182
|
Message: "\u6D88\u606F",
|
|
181
183
|
Monthly: "\u6BCF\u6708",
|
|
@@ -2483,7 +2485,7 @@ function cliRoot() {
|
|
|
2483
2485
|
}
|
|
2484
2486
|
throw new Error("Cannot locate the CLI installation.");
|
|
2485
2487
|
}
|
|
2486
|
-
function createWebRuntime(projectDir, storageDir, connection) {
|
|
2488
|
+
function createWebRuntime(projectDir, storageDir, connection, relatedProjects = () => []) {
|
|
2487
2489
|
const root = cliRoot();
|
|
2488
2490
|
const builtWorker = path5.join(root, "dist/web/worker.mjs");
|
|
2489
2491
|
const worker = existsSync(builtWorker) ? builtWorker : path5.join(root, "src/web/worker.mjs");
|
|
@@ -2595,7 +2597,7 @@ function createWebRuntime(projectDir, storageDir, connection) {
|
|
|
2595
2597
|
}
|
|
2596
2598
|
});
|
|
2597
2599
|
child.stdin.on("error", () => void 0);
|
|
2598
|
-
child.stdin.end(JSON.stringify({ ...request, storageDir, runtimeProjectDir: projectDir }));
|
|
2600
|
+
child.stdin.end(JSON.stringify({ ...request, storageDir, runtimeProjectDir: projectDir, relatedProjects: relatedProjects() }));
|
|
2599
2601
|
});
|
|
2600
2602
|
return {
|
|
2601
2603
|
available: true,
|
|
@@ -2728,12 +2730,13 @@ async function startWebServer(options) {
|
|
|
2728
2730
|
let login;
|
|
2729
2731
|
let passwordLogin = false;
|
|
2730
2732
|
let hostConnection = options.host?.connection;
|
|
2731
|
-
let hostManaged =
|
|
2733
|
+
let hostManaged = options.host !== void 0;
|
|
2732
2734
|
let authEpoch = 0;
|
|
2733
2735
|
let authenticationInitialized = false;
|
|
2734
2736
|
let authCheck;
|
|
2735
2737
|
const sensitiveTokens = /* @__PURE__ */ new Set();
|
|
2736
|
-
|
|
2738
|
+
let relatedProjects = options.host?.relatedProjects ?? [];
|
|
2739
|
+
const runtime = options.runtime ?? createWebRuntime(projectDir, storageDir, () => connection, () => relatedProjects);
|
|
2737
2740
|
let inspected;
|
|
2738
2741
|
try {
|
|
2739
2742
|
inspected = await inspectWebProject(runtime, projectDir, options.host);
|
|
@@ -2891,7 +2894,7 @@ async function startWebServer(options) {
|
|
|
2891
2894
|
return;
|
|
2892
2895
|
}
|
|
2893
2896
|
const readingEpoch = authEpoch;
|
|
2894
|
-
const config = await readCliAuthConfig();
|
|
2897
|
+
const config = hostManaged ? createEmptyWorkflowAuthConfig() : await readCliAuthConfig();
|
|
2895
2898
|
if (readingEpoch !== authEpoch) {
|
|
2896
2899
|
return;
|
|
2897
2900
|
}
|
|
@@ -3220,6 +3223,9 @@ ${token}`;
|
|
|
3220
3223
|
break;
|
|
3221
3224
|
}
|
|
3222
3225
|
case "login": {
|
|
3226
|
+
if (hostManaged) {
|
|
3227
|
+
throw new Error(t("Manage this account through Studio."));
|
|
3228
|
+
}
|
|
3223
3229
|
if (!options.host && process.env.WORKFLOW_SERVER_ADMIN_KEY !== void 0) {
|
|
3224
3230
|
throw new Error("This session is supplied by the host. Sign in through the host.");
|
|
3225
3231
|
}
|
|
@@ -3275,15 +3281,19 @@ ${token}`;
|
|
|
3275
3281
|
case "verify":
|
|
3276
3282
|
await refreshAuth(true);
|
|
3277
3283
|
break;
|
|
3278
|
-
case "logout":
|
|
3284
|
+
case "logout": {
|
|
3285
|
+
if (hostManaged) {
|
|
3286
|
+
throw new Error(t("Manage this account through Studio."));
|
|
3287
|
+
}
|
|
3279
3288
|
signedOut = true;
|
|
3280
3289
|
login?.kill();
|
|
3281
|
-
if (
|
|
3290
|
+
if (process.env.WORKFLOW_SERVER_ADMIN_KEY === void 0) {
|
|
3282
3291
|
await clearCliAuthConfig();
|
|
3283
3292
|
}
|
|
3284
3293
|
authIdentity = "signed-out";
|
|
3285
3294
|
await refreshAuth();
|
|
3286
3295
|
break;
|
|
3296
|
+
}
|
|
3287
3297
|
default:
|
|
3288
3298
|
throw new Error(`Unsupported action: ${message.type}`);
|
|
3289
3299
|
}
|
|
@@ -3391,7 +3401,7 @@ ${token}`;
|
|
|
3391
3401
|
const ancestors = options.host ? "'self' vscode-webview: vscode-file: https://*.vscode-cdn.net https://*.vscode-webview.net http://127.0.0.1:* http://localhost:*" : "'self' vscode-webview:";
|
|
3392
3402
|
response.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store", "Content-Security-Policy": `default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https: http:; font-src 'self'; connect-src 'self'; media-src 'self' blob:; frame-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors ${ancestors}; form-action 'self'` });
|
|
3393
3403
|
const ui = isKanban ? "kanban-workspace" : isWorkflow ? "workflow-workspace" : "workflow-ui";
|
|
3394
|
-
response.end(`<!doctype html><html lang="${locale()}" data-theme="${options.theme ?? "auto"}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="workflow-project" content="${key}"><title>Workflow</title><link rel="stylesheet" href="/assets/codicons/codicon.css"><link rel="stylesheet" href="/assets/${ui}.css"><link rel="stylesheet" href="/assets/theme.css"></head><body data-surface="${options.host?.surface ?? "editor"}"><div id="app"></div><script src="/assets/${ui}.js" defer></script></body></html>`);
|
|
3404
|
+
response.end(`<!doctype html><html lang="${locale()}" data-theme="${options.theme ?? "auto"}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="workflow-project" content="${key}"><title>Workflow</title><link rel="stylesheet" href="/assets/codicons/codicon.css"><link rel="stylesheet" href="/assets/${ui}.css"><link rel="stylesheet" href="/assets/theme.css"></head><body data-surface="${options.host?.surface ?? "editor"}" data-account-host="${hostManaged}"><div id="app"></div><script src="/assets/${ui}.js" defer></script></body></html>`);
|
|
3395
3405
|
return;
|
|
3396
3406
|
}
|
|
3397
3407
|
const assets = { "/assets/workflow-ui.js": "application/javascript", "/assets/workflow-ui.css": "text/css", "/assets/theme.css": "text/css", "/assets/codicons/codicon.css": "text/css", "/assets/codicons/codicon.ttf": "font/ttf" };
|
|
@@ -3439,6 +3449,9 @@ ${token}`;
|
|
|
3439
3449
|
controller,
|
|
3440
3450
|
workflowController,
|
|
3441
3451
|
kanbanController,
|
|
3452
|
+
setHostRelatedProjects: (value) => {
|
|
3453
|
+
relatedProjects = value;
|
|
3454
|
+
},
|
|
3442
3455
|
setHostConnection: async (value) => {
|
|
3443
3456
|
authEpoch++;
|
|
3444
3457
|
authCheck?.abort();
|
|
@@ -3477,6 +3490,23 @@ import { createInterface } from "readline";
|
|
|
3477
3490
|
|
|
3478
3491
|
// src/web/host-config.ts
|
|
3479
3492
|
import path7 from "path";
|
|
3493
|
+
function parseHostRelatedProjects(value) {
|
|
3494
|
+
if (value === void 0) {
|
|
3495
|
+
return [];
|
|
3496
|
+
}
|
|
3497
|
+
if (!Array.isArray(value) || value.length > 100) {
|
|
3498
|
+
throw new Error("Invalid host related projects.");
|
|
3499
|
+
}
|
|
3500
|
+
const aliases = /* @__PURE__ */ new Set(), directories = /* @__PURE__ */ new Set();
|
|
3501
|
+
return value.map((project) => {
|
|
3502
|
+
if (!project || typeof project.alias !== "string" || !/^[a-z][a-z0-9_-]{0,63}$/.test(project.alias) || typeof project.projectDir !== "string" || !path7.isAbsolute(project.projectDir) || aliases.has(project.alias) || directories.has(project.projectDir)) {
|
|
3503
|
+
throw new Error("Invalid host related projects.");
|
|
3504
|
+
}
|
|
3505
|
+
aliases.add(project.alias);
|
|
3506
|
+
directories.add(project.projectDir);
|
|
3507
|
+
return { alias: project.alias, projectDir: project.projectDir };
|
|
3508
|
+
});
|
|
3509
|
+
}
|
|
3480
3510
|
function parseHostConnection(value) {
|
|
3481
3511
|
if (value === null || value === void 0) {
|
|
3482
3512
|
return;
|
|
@@ -3510,7 +3540,13 @@ function parseHostConfiguration(value) {
|
|
|
3510
3540
|
if (configuration.surface !== "assistant" && (assistant || assistantProjectDir !== void 0)) {
|
|
3511
3541
|
throw new Error("Assistant configuration requires the assistant surface.");
|
|
3512
3542
|
}
|
|
3513
|
-
return {
|
|
3543
|
+
return {
|
|
3544
|
+
surface: configuration.surface,
|
|
3545
|
+
assistant,
|
|
3546
|
+
assistantProjectDir,
|
|
3547
|
+
connection: parseHostConnection(configuration.connection),
|
|
3548
|
+
relatedProjects: parseHostRelatedProjects(configuration.relatedProjects)
|
|
3549
|
+
};
|
|
3514
3550
|
}
|
|
3515
3551
|
|
|
3516
3552
|
// src/web.ts
|
|
@@ -3604,7 +3640,7 @@ async function main(argv) {
|
|
|
3604
3640
|
};
|
|
3605
3641
|
process.once("SIGINT", close);
|
|
3606
3642
|
process.once("SIGTERM", close);
|
|
3607
|
-
console.log(options.json ? JSON.stringify({ type: "ready", protocolVersion: 1, hostProtocolVersion: 1, traceProtocolVersion: 1, assistantProtocolVersion: host?.assistant ? ASSISTANT_PROTOCOL_VERSION : void 0, url: app.url, project: { id: app.project.id, name: app.project.name, projectType: app.project.projectType }, runtime: app.project.runtime }) : `Workflow Web: ${app.url}`);
|
|
3643
|
+
console.log(options.json ? JSON.stringify({ type: "ready", protocolVersion: 1, hostProtocolVersion: 1, accountProtocolVersion: 1, relatedProjectsProtocolVersion: 1, traceProtocolVersion: 1, assistantProtocolVersion: host?.assistant ? ASSISTANT_PROTOCOL_VERSION : void 0, url: app.url, project: { id: app.project.id, name: app.project.name, projectType: app.project.projectType }, runtime: app.project.runtime }) : `Workflow Web: ${app.url}`);
|
|
3608
3644
|
if (lines) {
|
|
3609
3645
|
void (async () => {
|
|
3610
3646
|
try {
|
|
@@ -3619,6 +3655,8 @@ async function main(argv) {
|
|
|
3619
3655
|
process.exitCode = 1;
|
|
3620
3656
|
close();
|
|
3621
3657
|
});
|
|
3658
|
+
} else if (message.type === "related-projects") {
|
|
3659
|
+
app.setHostRelatedProjects(parseHostRelatedProjects(message.relatedProjects));
|
|
3622
3660
|
} else if (message.type === "close") {
|
|
3623
3661
|
break;
|
|
3624
3662
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workflow-code/cli",
|
|
3
|
-
"version": "0.2.4-
|
|
3
|
+
"version": "0.2.4-20261012005.codex-studio-integration",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"workflow-code": "./dist/index.js"
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"@workflow-code/kanban-project": "0.1.0",
|
|
43
43
|
"@workflow-code/openai-responses": "0.1.0",
|
|
44
44
|
"@workflow-code/project-dependencies": "0.1.0",
|
|
45
|
-
"@workflow-code/project-upload": "0.1.0",
|
|
46
45
|
"@workflow-code/project-info": "0.1.0",
|
|
46
|
+
"@workflow-code/project-upload": "0.1.0",
|
|
47
47
|
"@workflow-code/runtime-compatibility": "0.1.0",
|
|
48
48
|
"@workflow-code/workflow-ids": "0.1.0",
|
|
49
49
|
"@workflow-code/workflow-mcp": "0.1.0"
|