@workflow-code/cli 0.1.1 → 0.1.3
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-ONAQ5F5R.js +10822 -0
- package/dist/index.js +13 -22
- package/dist/{local-XBULJ25O.js → local-ZIUEP53J.js} +46 -33
- package/dist/workspace-MLJHPOLS.js +2501 -0
- package/package.json +2 -1
- package/dist/workspace-WJOCEGA6.js +0 -1044
|
@@ -0,0 +1,2501 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
__commonJS,
|
|
4
|
+
__toESM,
|
|
5
|
+
getCurrentLocale,
|
|
6
|
+
i18n,
|
|
7
|
+
prepareCliInvocation,
|
|
8
|
+
translateWorkflowCodeError
|
|
9
|
+
} from "./chunk-ONAQ5F5R.js";
|
|
10
|
+
|
|
11
|
+
// ../../node_modules/.pnpm/ignore@7.0.6/node_modules/ignore/index.js
|
|
12
|
+
var require_ignore = __commonJS({
|
|
13
|
+
"../../node_modules/.pnpm/ignore@7.0.6/node_modules/ignore/index.js"(exports, module) {
|
|
14
|
+
"use strict";
|
|
15
|
+
function makeArray(subject) {
|
|
16
|
+
return Array.isArray(subject) ? subject : [subject];
|
|
17
|
+
}
|
|
18
|
+
var UNDEFINED = void 0;
|
|
19
|
+
var EMPTY = "";
|
|
20
|
+
var SPACE = " ";
|
|
21
|
+
var ESCAPE = "\\";
|
|
22
|
+
var REGEX_TEST_BLANK_LINE = /^\s+$/;
|
|
23
|
+
var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
|
|
24
|
+
var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
|
|
25
|
+
var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
|
|
26
|
+
var REGEX_SPLITALL_CRLF = /\r?\n/g;
|
|
27
|
+
var REGEX_TEST_INVALID_PATH = /^\.{0,2}\/|^\.{1,2}$/;
|
|
28
|
+
var REGEX_TEST_TRAILING_SLASH = /\/$/;
|
|
29
|
+
var SLASH = "/";
|
|
30
|
+
var TMP_KEY_IGNORE = "node-ignore";
|
|
31
|
+
if (typeof Symbol !== "undefined") {
|
|
32
|
+
TMP_KEY_IGNORE = /* @__PURE__ */ Symbol.for("node-ignore");
|
|
33
|
+
}
|
|
34
|
+
var KEY_IGNORE = TMP_KEY_IGNORE;
|
|
35
|
+
var define = (object, key, value) => {
|
|
36
|
+
Object.defineProperty(object, key, { value });
|
|
37
|
+
return value;
|
|
38
|
+
};
|
|
39
|
+
var REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
|
|
40
|
+
var RETURN_FALSE = () => false;
|
|
41
|
+
var sanitizeRange = (range) => range.replace(
|
|
42
|
+
REGEX_REGEXP_RANGE,
|
|
43
|
+
(match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) ? match : EMPTY
|
|
44
|
+
);
|
|
45
|
+
var negateRange = (range) => range.startsWith("!") || range.startsWith("\\^") ? `^${range.slice(range[0] === "!" ? 1 : 2)}` : range;
|
|
46
|
+
var cleanRangeBackSlash = (slashes) => {
|
|
47
|
+
const { length } = slashes;
|
|
48
|
+
return slashes.slice(0, length - length % 2);
|
|
49
|
+
};
|
|
50
|
+
var REPLACERS = [
|
|
51
|
+
[
|
|
52
|
+
// Remove BOM
|
|
53
|
+
// TODO:
|
|
54
|
+
// Other similar zero-width characters?
|
|
55
|
+
/^\uFEFF/,
|
|
56
|
+
() => EMPTY
|
|
57
|
+
],
|
|
58
|
+
// > Trailing spaces are ignored unless they are quoted with backslash ("\")
|
|
59
|
+
[
|
|
60
|
+
// (a\ ) -> (a )
|
|
61
|
+
// (a ) -> (a)
|
|
62
|
+
// (a ) -> (a)
|
|
63
|
+
// (a \ ) -> (a )
|
|
64
|
+
/((?:\\\\)*?)(\\?\s+)$/,
|
|
65
|
+
(_, m1, m2) => m1 + (m2.indexOf("\\") === 0 ? SPACE : EMPTY)
|
|
66
|
+
],
|
|
67
|
+
// Replace (\ ) with ' '
|
|
68
|
+
// (\ ) -> ' '
|
|
69
|
+
// (\\ ) -> '\\ '
|
|
70
|
+
// (\\\ ) -> '\\ '
|
|
71
|
+
[
|
|
72
|
+
/(\\+?)\s/g,
|
|
73
|
+
(_, m1) => {
|
|
74
|
+
const { length } = m1;
|
|
75
|
+
return m1.slice(0, length - length % 2) + SPACE;
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
// Escape metacharacters
|
|
79
|
+
// which is written down by users but means special for regular expressions.
|
|
80
|
+
// > There are 12 characters with special meanings:
|
|
81
|
+
// > - the backslash \,
|
|
82
|
+
// > - the caret ^,
|
|
83
|
+
// > - the dollar sign $,
|
|
84
|
+
// > - the period or dot .,
|
|
85
|
+
// > - the vertical bar or pipe symbol |,
|
|
86
|
+
// > - the question mark ?,
|
|
87
|
+
// > - the asterisk or star *,
|
|
88
|
+
// > - the plus sign +,
|
|
89
|
+
// > - the opening parenthesis (,
|
|
90
|
+
// > - the closing parenthesis ),
|
|
91
|
+
// > - and the opening square bracket [,
|
|
92
|
+
// > - the opening curly brace {,
|
|
93
|
+
// > These special characters are often called "metacharacters".
|
|
94
|
+
[
|
|
95
|
+
/[\\$.|*+(){^]/g,
|
|
96
|
+
(match) => `\\${match}`
|
|
97
|
+
],
|
|
98
|
+
[
|
|
99
|
+
// > a question mark (?) matches a single character
|
|
100
|
+
/(?!\\)\?/g,
|
|
101
|
+
() => "[^/]"
|
|
102
|
+
],
|
|
103
|
+
// leading slash
|
|
104
|
+
[
|
|
105
|
+
// > A leading slash matches the beginning of the pathname.
|
|
106
|
+
// > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
|
|
107
|
+
// A leading slash matches the beginning of the pathname
|
|
108
|
+
/^\//,
|
|
109
|
+
() => "^"
|
|
110
|
+
],
|
|
111
|
+
// replace special metacharacter slash after the leading slash
|
|
112
|
+
[
|
|
113
|
+
/\//g,
|
|
114
|
+
() => "\\/"
|
|
115
|
+
],
|
|
116
|
+
[
|
|
117
|
+
// > A leading "**" followed by a slash means match in all directories.
|
|
118
|
+
// > For example, "**/foo" matches file or directory "foo" anywhere,
|
|
119
|
+
// > the same as pattern "foo".
|
|
120
|
+
// > "**/foo/bar" matches file or directory "bar" anywhere that is directly
|
|
121
|
+
// > under directory "foo".
|
|
122
|
+
// Notice that the '*'s have been replaced as '\\*'
|
|
123
|
+
/^\^*(?:\\\*\\\*\\\/)+/,
|
|
124
|
+
// '**/foo' <-> 'foo'
|
|
125
|
+
() => "^(?:.*\\/)?"
|
|
126
|
+
],
|
|
127
|
+
// starting
|
|
128
|
+
[
|
|
129
|
+
// there will be no leading '/'
|
|
130
|
+
// (which has been replaced by section "leading slash")
|
|
131
|
+
// If starts with '**', adding a '^' to the regular expression also works
|
|
132
|
+
/^(?=[^^])/,
|
|
133
|
+
function startingReplacer() {
|
|
134
|
+
return !/\/(?!$)/.test(this) ? "(?:^|\\/)" : "^";
|
|
135
|
+
}
|
|
136
|
+
],
|
|
137
|
+
// two globstars
|
|
138
|
+
[
|
|
139
|
+
// Use lookahead assertions so that we could match more than one `'/**'`
|
|
140
|
+
/\\\/\\\*\\\*(?=\\\/|$)/g,
|
|
141
|
+
// Zero, one or several directories
|
|
142
|
+
// should not use '*', or it will be replaced by the next replacer
|
|
143
|
+
// Check if it is not the last `'/**'`
|
|
144
|
+
(_, index, str) => index + 6 < str.length ? "(?:\\/[^\\/]+)*" : "\\/.+"
|
|
145
|
+
],
|
|
146
|
+
// normal intermediate wildcards
|
|
147
|
+
[
|
|
148
|
+
// Never replace escaped '*'
|
|
149
|
+
// ignore rule '\*' will match the path '*'
|
|
150
|
+
// 'abc.*/' -> go
|
|
151
|
+
// 'abc.*' -> skip this rule,
|
|
152
|
+
// coz trailing single wildcard will be handed by [trailing wildcard]
|
|
153
|
+
/(^|[^\\]+)(\\\*)+(?=.+)/g,
|
|
154
|
+
// '*.js' matches '.js'
|
|
155
|
+
// '*.js' doesn't match 'abc'
|
|
156
|
+
(_, p1, p2) => {
|
|
157
|
+
const unescaped = p2.replace(/\\\*/g, "[^\\/]*");
|
|
158
|
+
return p1 + unescaped;
|
|
159
|
+
}
|
|
160
|
+
],
|
|
161
|
+
[
|
|
162
|
+
// unescape, revert step 3 except for back slash
|
|
163
|
+
// For example, if a user escape a '\\*',
|
|
164
|
+
// after step 3, the result will be '\\\\\\*'
|
|
165
|
+
/\\\\\\(?=[$.|*+(){^])/g,
|
|
166
|
+
() => ESCAPE
|
|
167
|
+
],
|
|
168
|
+
[
|
|
169
|
+
// '\\\\' -> '\\'
|
|
170
|
+
/\\\\/g,
|
|
171
|
+
() => ESCAPE
|
|
172
|
+
],
|
|
173
|
+
[
|
|
174
|
+
// > The range notation, e.g. [a-zA-Z],
|
|
175
|
+
// > can be used to match one of the characters in a range.
|
|
176
|
+
// `\` is escaped by step 3
|
|
177
|
+
/(\\)?\[([^\]/]*?)(\\*)($|\])/g,
|
|
178
|
+
(match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}` : close === "]" ? endEscape.length % 2 === 0 ? `[${negateRange(sanitizeRange(range))}${endEscape}]` : "[]" : "[]"
|
|
179
|
+
],
|
|
180
|
+
// ending
|
|
181
|
+
[
|
|
182
|
+
// 'js' will not match 'js.'
|
|
183
|
+
// 'ab' will not match 'abc'
|
|
184
|
+
/(?:[^*])$/,
|
|
185
|
+
// WTF!
|
|
186
|
+
// https://git-scm.com/docs/gitignore
|
|
187
|
+
// changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
|
|
188
|
+
// which re-fixes #24, #38
|
|
189
|
+
// > If there is a separator at the end of the pattern then the pattern
|
|
190
|
+
// > will only match directories, otherwise the pattern can match both
|
|
191
|
+
// > files and directories.
|
|
192
|
+
// 'js*' will not match 'a.js'
|
|
193
|
+
// 'js/' will not match 'a.js'
|
|
194
|
+
// 'js' will match 'a.js' and 'a.js/'
|
|
195
|
+
(match) => /\/$/.test(match) ? `${match}$` : `${match}(?=$|\\/$)`
|
|
196
|
+
]
|
|
197
|
+
];
|
|
198
|
+
var REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\\\*$/;
|
|
199
|
+
var MODE_IGNORE = "regex";
|
|
200
|
+
var MODE_CHECK_IGNORE = "checkRegex";
|
|
201
|
+
var UNDERSCORE = "_";
|
|
202
|
+
var TRAILING_WILD_CARD_REPLACERS = {
|
|
203
|
+
[MODE_IGNORE](_, p1) {
|
|
204
|
+
const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
|
|
205
|
+
return `${prefix}(?=$|\\/$)`;
|
|
206
|
+
},
|
|
207
|
+
[MODE_CHECK_IGNORE](_, p1) {
|
|
208
|
+
const prefix = p1 ? `${p1}[^/]*` : "[^/]*";
|
|
209
|
+
return `${prefix}(?=$|\\/$)`;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
var makeRegexPrefix = (pattern) => REPLACERS.reduce(
|
|
213
|
+
(prev, [matcher, replacer]) => prev.replace(matcher, replacer.bind(pattern)),
|
|
214
|
+
pattern
|
|
215
|
+
);
|
|
216
|
+
var isString = (subject) => typeof subject === "string";
|
|
217
|
+
var checkPattern = (pattern) => pattern && isString(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0;
|
|
218
|
+
var splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF).filter(Boolean);
|
|
219
|
+
var IgnoreRule = class {
|
|
220
|
+
constructor(pattern, mark, body, ignoreCase, negative, prefix) {
|
|
221
|
+
this.pattern = pattern;
|
|
222
|
+
this.mark = mark;
|
|
223
|
+
this.negative = negative;
|
|
224
|
+
define(this, "body", body);
|
|
225
|
+
define(this, "ignoreCase", ignoreCase);
|
|
226
|
+
define(this, "regexPrefix", prefix);
|
|
227
|
+
}
|
|
228
|
+
get regex() {
|
|
229
|
+
const key = UNDERSCORE + MODE_IGNORE;
|
|
230
|
+
if (this[key]) {
|
|
231
|
+
return this[key];
|
|
232
|
+
}
|
|
233
|
+
return this._make(MODE_IGNORE, key);
|
|
234
|
+
}
|
|
235
|
+
get checkRegex() {
|
|
236
|
+
const key = UNDERSCORE + MODE_CHECK_IGNORE;
|
|
237
|
+
if (this[key]) {
|
|
238
|
+
return this[key];
|
|
239
|
+
}
|
|
240
|
+
return this._make(MODE_CHECK_IGNORE, key);
|
|
241
|
+
}
|
|
242
|
+
_make(mode, key) {
|
|
243
|
+
const str = this.regexPrefix.replace(
|
|
244
|
+
REGEX_REPLACE_TRAILING_WILDCARD,
|
|
245
|
+
// It does not need to bind pattern
|
|
246
|
+
TRAILING_WILD_CARD_REPLACERS[mode]
|
|
247
|
+
);
|
|
248
|
+
const regex = this.ignoreCase ? new RegExp(str, "i") : new RegExp(str);
|
|
249
|
+
return define(this, key, regex);
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
var createRule = ({
|
|
253
|
+
pattern,
|
|
254
|
+
mark
|
|
255
|
+
}, ignoreCase) => {
|
|
256
|
+
let negative = false;
|
|
257
|
+
let body = pattern;
|
|
258
|
+
if (body.indexOf("!") === 0) {
|
|
259
|
+
negative = true;
|
|
260
|
+
body = body.substr(1);
|
|
261
|
+
}
|
|
262
|
+
body = body.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, "!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, "#");
|
|
263
|
+
const regexPrefix = makeRegexPrefix(body);
|
|
264
|
+
return new IgnoreRule(
|
|
265
|
+
pattern,
|
|
266
|
+
mark,
|
|
267
|
+
body,
|
|
268
|
+
ignoreCase,
|
|
269
|
+
negative,
|
|
270
|
+
regexPrefix
|
|
271
|
+
);
|
|
272
|
+
};
|
|
273
|
+
var RuleManager = class {
|
|
274
|
+
constructor(ignoreCase) {
|
|
275
|
+
this._ignoreCase = ignoreCase;
|
|
276
|
+
this._rules = [];
|
|
277
|
+
}
|
|
278
|
+
_add(pattern) {
|
|
279
|
+
if (pattern && pattern[KEY_IGNORE]) {
|
|
280
|
+
this._rules = this._rules.concat(pattern._rules._rules);
|
|
281
|
+
this._added = true;
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (isString(pattern)) {
|
|
285
|
+
pattern = {
|
|
286
|
+
pattern
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
if (checkPattern(pattern.pattern)) {
|
|
290
|
+
const rule = createRule(pattern, this._ignoreCase);
|
|
291
|
+
this._added = true;
|
|
292
|
+
this._rules.push(rule);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
// @param {Array<string> | string | Ignore} pattern
|
|
296
|
+
add(pattern) {
|
|
297
|
+
this._added = false;
|
|
298
|
+
makeArray(
|
|
299
|
+
isString(pattern) ? splitPattern(pattern) : pattern
|
|
300
|
+
).forEach(this._add, this);
|
|
301
|
+
return this._added;
|
|
302
|
+
}
|
|
303
|
+
// Test one single path without recursively checking parent directories
|
|
304
|
+
//
|
|
305
|
+
// - checkUnignored `boolean` whether should check if the path is unignored,
|
|
306
|
+
// setting `checkUnignored` to `false` could reduce additional
|
|
307
|
+
// path matching.
|
|
308
|
+
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
309
|
+
// @returns {TestResult} true if a file is ignored
|
|
310
|
+
test(path5, checkUnignored, mode) {
|
|
311
|
+
let ignored = false;
|
|
312
|
+
let unignored = false;
|
|
313
|
+
let matchedRule;
|
|
314
|
+
this._rules.forEach((rule) => {
|
|
315
|
+
const { negative } = rule;
|
|
316
|
+
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const matched = rule[mode].test(path5);
|
|
320
|
+
if (!matched) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
ignored = !negative;
|
|
324
|
+
unignored = negative;
|
|
325
|
+
matchedRule = negative ? UNDEFINED : rule;
|
|
326
|
+
});
|
|
327
|
+
const ret = {
|
|
328
|
+
ignored,
|
|
329
|
+
unignored
|
|
330
|
+
};
|
|
331
|
+
if (matchedRule) {
|
|
332
|
+
ret.rule = matchedRule;
|
|
333
|
+
}
|
|
334
|
+
return ret;
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
var throwError = (message, Ctor) => {
|
|
338
|
+
throw new Ctor(message);
|
|
339
|
+
};
|
|
340
|
+
var checkPath = (path5, originalPath, doThrow) => {
|
|
341
|
+
if (!isString(path5)) {
|
|
342
|
+
return doThrow(
|
|
343
|
+
`path must be a string, but got \`${originalPath}\``,
|
|
344
|
+
TypeError
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
if (!path5) {
|
|
348
|
+
return doThrow(`path must not be empty`, TypeError);
|
|
349
|
+
}
|
|
350
|
+
if (checkPath.isNotRelative(path5)) {
|
|
351
|
+
const r = "`path.relative()`d";
|
|
352
|
+
return doThrow(
|
|
353
|
+
`path should be a ${r} string, but got "${originalPath}"`,
|
|
354
|
+
RangeError
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
return true;
|
|
358
|
+
};
|
|
359
|
+
var isNotRelative = (path5) => REGEX_TEST_INVALID_PATH.test(path5);
|
|
360
|
+
checkPath.isNotRelative = isNotRelative;
|
|
361
|
+
checkPath.convert = (p) => p;
|
|
362
|
+
var Ignore = class {
|
|
363
|
+
constructor({
|
|
364
|
+
ignorecase = true,
|
|
365
|
+
ignoreCase = ignorecase,
|
|
366
|
+
allowRelativePaths = false
|
|
367
|
+
} = {}) {
|
|
368
|
+
define(this, KEY_IGNORE, true);
|
|
369
|
+
this._rules = new RuleManager(ignoreCase);
|
|
370
|
+
this._strictPathCheck = !allowRelativePaths;
|
|
371
|
+
this._initCache();
|
|
372
|
+
}
|
|
373
|
+
_initCache() {
|
|
374
|
+
this._ignoreCache = /* @__PURE__ */ Object.create(null);
|
|
375
|
+
this._testCache = /* @__PURE__ */ Object.create(null);
|
|
376
|
+
}
|
|
377
|
+
add(pattern) {
|
|
378
|
+
if (this._rules.add(pattern)) {
|
|
379
|
+
this._initCache();
|
|
380
|
+
}
|
|
381
|
+
return this;
|
|
382
|
+
}
|
|
383
|
+
// legacy
|
|
384
|
+
addPattern(pattern) {
|
|
385
|
+
return this.add(pattern);
|
|
386
|
+
}
|
|
387
|
+
// @returns {TestResult}
|
|
388
|
+
_test(originalPath, cache, checkUnignored, slices) {
|
|
389
|
+
const path5 = originalPath && checkPath.convert(originalPath);
|
|
390
|
+
checkPath(
|
|
391
|
+
path5,
|
|
392
|
+
originalPath,
|
|
393
|
+
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
394
|
+
);
|
|
395
|
+
return this._t(path5, cache, checkUnignored, slices);
|
|
396
|
+
}
|
|
397
|
+
checkIgnore(path5) {
|
|
398
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path5)) {
|
|
399
|
+
return this.test(path5);
|
|
400
|
+
}
|
|
401
|
+
const slices = path5.split(SLASH).filter(Boolean);
|
|
402
|
+
slices.pop();
|
|
403
|
+
if (slices.length) {
|
|
404
|
+
const parent = this._t(
|
|
405
|
+
slices.join(SLASH) + SLASH,
|
|
406
|
+
this._testCache,
|
|
407
|
+
true,
|
|
408
|
+
slices
|
|
409
|
+
);
|
|
410
|
+
if (parent.ignored) {
|
|
411
|
+
return parent;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return this._rules.test(path5, false, MODE_CHECK_IGNORE);
|
|
415
|
+
}
|
|
416
|
+
_t(path5, cache, checkUnignored, slices) {
|
|
417
|
+
if (path5 in cache) {
|
|
418
|
+
return cache[path5];
|
|
419
|
+
}
|
|
420
|
+
if (!slices) {
|
|
421
|
+
slices = path5.split(SLASH).filter(Boolean);
|
|
422
|
+
}
|
|
423
|
+
slices.pop();
|
|
424
|
+
if (!slices.length) {
|
|
425
|
+
return cache[path5] = this._rules.test(path5, checkUnignored, MODE_IGNORE);
|
|
426
|
+
}
|
|
427
|
+
const parent = this._t(
|
|
428
|
+
slices.join(SLASH) + SLASH,
|
|
429
|
+
cache,
|
|
430
|
+
checkUnignored,
|
|
431
|
+
slices
|
|
432
|
+
);
|
|
433
|
+
return cache[path5] = parent.ignored ? parent : this._rules.test(path5, checkUnignored, MODE_IGNORE);
|
|
434
|
+
}
|
|
435
|
+
ignores(path5) {
|
|
436
|
+
return this._test(path5, this._ignoreCache, false).ignored;
|
|
437
|
+
}
|
|
438
|
+
createFilter() {
|
|
439
|
+
return (path5) => !this.ignores(path5);
|
|
440
|
+
}
|
|
441
|
+
filter(paths) {
|
|
442
|
+
return makeArray(paths).filter(this.createFilter());
|
|
443
|
+
}
|
|
444
|
+
// @returns {TestResult}
|
|
445
|
+
test(path5) {
|
|
446
|
+
return this._test(path5, this._testCache, true);
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
var factory = (options) => new Ignore(options);
|
|
450
|
+
var isPathValid = (path5) => checkPath(path5 && checkPath.convert(path5), path5, RETURN_FALSE);
|
|
451
|
+
var setupWindows = () => {
|
|
452
|
+
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
453
|
+
checkPath.convert = makePosix;
|
|
454
|
+
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
455
|
+
checkPath.isNotRelative = (path5) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path5) || isNotRelative(path5);
|
|
456
|
+
};
|
|
457
|
+
if (
|
|
458
|
+
// Detect `process` so that it can run in browsers.
|
|
459
|
+
typeof process !== "undefined" && process.platform === "win32"
|
|
460
|
+
) {
|
|
461
|
+
setupWindows();
|
|
462
|
+
}
|
|
463
|
+
module.exports = factory;
|
|
464
|
+
factory.default = factory;
|
|
465
|
+
module.exports.isPathValid = isPathValid;
|
|
466
|
+
define(module.exports, /* @__PURE__ */ Symbol.for("setupWindows"), setupWindows);
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
// src/workspace.ts
|
|
471
|
+
import { spawn } from "child_process";
|
|
472
|
+
import { createHash, randomUUID } from "crypto";
|
|
473
|
+
import { existsSync as existsSync2 } from "fs";
|
|
474
|
+
import { copyFile as copyFile2, mkdir as mkdir3, readFile as readFile3, readdir as readdir2, rename, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
475
|
+
import path4 from "path";
|
|
476
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
477
|
+
|
|
478
|
+
// ../../shared/auth-config/index.ts
|
|
479
|
+
var WORKFLOW_AUTH_FILE_NAME = "workflow-auth.json";
|
|
480
|
+
var DEFAULT_MAC_PLATFORM = "darwin";
|
|
481
|
+
var DEFAULT_WINDOWS_PLATFORM = "win32";
|
|
482
|
+
var DEFAULT_OTHER_PLATFORM = "linux";
|
|
483
|
+
function createEmptyWorkflowAuthConfig() {
|
|
484
|
+
return {
|
|
485
|
+
serverUrl: "",
|
|
486
|
+
apiKey: ""
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
function normalizeWorkflowAuthConfig(value) {
|
|
490
|
+
if (!isRecord(value)) {
|
|
491
|
+
return createEmptyWorkflowAuthConfig();
|
|
492
|
+
}
|
|
493
|
+
return {
|
|
494
|
+
// issue #90 Accept legacy field names (remoteUrl, token) from pre-migration
|
|
495
|
+
// config files so existing users don't lose their credentials on upgrade.
|
|
496
|
+
serverUrl: readString(value.serverUrl) ?? readString(value.remoteUrl) ?? "",
|
|
497
|
+
apiKey: readString(value.apiKey) ?? readString(value.token) ?? "",
|
|
498
|
+
expiresAt: readString(value.expiresAt),
|
|
499
|
+
user: isRecord(value.user) ? {
|
|
500
|
+
userId: readString(value.user.userId) ?? "",
|
|
501
|
+
email: readString(value.user.email) ?? "",
|
|
502
|
+
displayName: readString(value.user.displayName) ?? "",
|
|
503
|
+
avatarUrl: readString(value.user.avatarUrl)
|
|
504
|
+
} : void 0,
|
|
505
|
+
source: value.source === "device_flow" || value.source === "manual" || value.source === "password" ? value.source : void 0
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
function isWorkflowAuthExpiringSoon(expiresAt, options = {}) {
|
|
509
|
+
if (!expiresAt) {
|
|
510
|
+
return false;
|
|
511
|
+
}
|
|
512
|
+
const expiresAtMs = new Date(expiresAt).getTime();
|
|
513
|
+
if (!Number.isFinite(expiresAtMs)) {
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
const now = options.now ?? Date.now();
|
|
517
|
+
const thresholdMs = options.thresholdMs ?? 1e3 * 60 * 60 * 24 * 3;
|
|
518
|
+
return expiresAtMs - now <= thresholdMs;
|
|
519
|
+
}
|
|
520
|
+
function resolveWorkflowCliAuthDir(env = readProcessEnv()) {
|
|
521
|
+
if (readString(env.WORKFLOW_CLI_AUTH_DIR)) {
|
|
522
|
+
return normalizePath(env.WORKFLOW_CLI_AUTH_DIR);
|
|
523
|
+
}
|
|
524
|
+
if (readPlatform(env) === DEFAULT_WINDOWS_PLATFORM) {
|
|
525
|
+
return joinPath(
|
|
526
|
+
env.APPDATA ?? joinPath(resolveHomeDir(env), "AppData", "Roaming"),
|
|
527
|
+
"workflow-code"
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
if (readPlatform(env) === DEFAULT_MAC_PLATFORM) {
|
|
531
|
+
return joinPath(resolveHomeDir(env), "Library", "Application Support", "workflow-code");
|
|
532
|
+
}
|
|
533
|
+
return joinPath(
|
|
534
|
+
env.XDG_CONFIG_HOME ?? joinPath(resolveHomeDir(env), ".config"),
|
|
535
|
+
"workflow-code"
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
function resolveWorkflowCliAuthFilePath(env = readProcessEnv()) {
|
|
539
|
+
return joinPath(resolveWorkflowCliAuthDir(env), WORKFLOW_AUTH_FILE_NAME);
|
|
540
|
+
}
|
|
541
|
+
function resolveHomeDir(env) {
|
|
542
|
+
return env.HOME ?? "";
|
|
543
|
+
}
|
|
544
|
+
function readPlatform(env) {
|
|
545
|
+
return env.platform ?? readProcessPlatform();
|
|
546
|
+
}
|
|
547
|
+
function isRecord(value) {
|
|
548
|
+
return typeof value === "object" && value !== null;
|
|
549
|
+
}
|
|
550
|
+
function readString(value) {
|
|
551
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
|
|
552
|
+
}
|
|
553
|
+
function joinPath(...parts) {
|
|
554
|
+
return normalizePath(parts.filter((part) => part !== "").join("/"));
|
|
555
|
+
}
|
|
556
|
+
function normalizePath(value) {
|
|
557
|
+
const normalized = value.replaceAll("\\", "/");
|
|
558
|
+
if (/^[A-Za-z]:\//.test(normalized)) {
|
|
559
|
+
const drive = normalized.slice(0, 2);
|
|
560
|
+
const rest = normalized.slice(2).replace(/\/+/g, "/");
|
|
561
|
+
return `${drive}${rest.startsWith("/") ? rest : `/${rest}`}`;
|
|
562
|
+
}
|
|
563
|
+
return normalized.replace(/\/+/g, "/");
|
|
564
|
+
}
|
|
565
|
+
function readProcessEnv() {
|
|
566
|
+
const processValue = readGlobalProcess();
|
|
567
|
+
return {
|
|
568
|
+
...processValue?.env ?? {},
|
|
569
|
+
platform: processValue?.platform ?? DEFAULT_OTHER_PLATFORM
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
function readProcessPlatform() {
|
|
573
|
+
return readGlobalProcess()?.platform ?? DEFAULT_OTHER_PLATFORM;
|
|
574
|
+
}
|
|
575
|
+
function readGlobalProcess() {
|
|
576
|
+
const processValue = Reflect.get(globalThis, "process");
|
|
577
|
+
if (typeof processValue !== "object" || processValue === null) {
|
|
578
|
+
return void 0;
|
|
579
|
+
}
|
|
580
|
+
return processValue;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// ../../shared/workflow-ids/index.ts
|
|
584
|
+
import path from "path";
|
|
585
|
+
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;
|
|
586
|
+
function isWorkflowUuid(value) {
|
|
587
|
+
return WORKFLOW_UUID_PATTERN.test(value.trim());
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// ../../shared/project-upload/node.ts
|
|
591
|
+
import { copyFile, lstat, mkdir, readFile, readdir } from "fs/promises";
|
|
592
|
+
import path2 from "path";
|
|
593
|
+
|
|
594
|
+
// ../../shared/project-upload/index.ts
|
|
595
|
+
var import_ignore = __toESM(require_ignore(), 1);
|
|
596
|
+
|
|
597
|
+
// ../../shared/kanban-project/index.ts
|
|
598
|
+
var KANBAN_MANIFEST_FILE = "kanban.json";
|
|
599
|
+
var KANBAN_ENTRY_FILE = "index.html";
|
|
600
|
+
var KANBAN_ARTIFACT_DIR = ".";
|
|
601
|
+
var KANBAN_DATA_SOURCE_ID_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
602
|
+
var KANBAN_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;
|
|
603
|
+
var MAX_KANBAN_DATA_SOURCES = 32;
|
|
604
|
+
var MAX_KANBAN_DATA_SOURCE_PREFIXES = 32;
|
|
605
|
+
var MAX_KANBAN_DATA_SOURCE_KEY_LENGTH = 1024;
|
|
606
|
+
function readPackageProjectType(value) {
|
|
607
|
+
if (!isRecord2(value)) return void 0;
|
|
608
|
+
const workflowCode = value["workflowCode"];
|
|
609
|
+
if (!isRecord2(workflowCode)) return void 0;
|
|
610
|
+
return workflowCode["projectType"] === "kanban" ? "kanban" : void 0;
|
|
611
|
+
}
|
|
612
|
+
function readKanbanArtifactConfig(value) {
|
|
613
|
+
if (!isRecord2(value)) {
|
|
614
|
+
throw new Error("package.json root must be an object.");
|
|
615
|
+
}
|
|
616
|
+
const workflowCode = value["workflowCode"];
|
|
617
|
+
if (!isRecord2(workflowCode)) {
|
|
618
|
+
throw new Error("package.json workflowCode must be an object for a Kanban project.");
|
|
619
|
+
}
|
|
620
|
+
const rawConfig = workflowCode["kanban"];
|
|
621
|
+
if (rawConfig !== void 0 && !isRecord2(rawConfig)) {
|
|
622
|
+
throw new Error("package.json workflowCode.kanban must be an object.");
|
|
623
|
+
}
|
|
624
|
+
return {
|
|
625
|
+
artifactDir: readKanbanRelativePath(
|
|
626
|
+
rawConfig?.["artifactDir"],
|
|
627
|
+
"workflowCode.kanban.artifactDir",
|
|
628
|
+
KANBAN_ARTIFACT_DIR,
|
|
629
|
+
true
|
|
630
|
+
),
|
|
631
|
+
entry: readKanbanRelativePath(
|
|
632
|
+
rawConfig?.["entry"],
|
|
633
|
+
"workflowCode.kanban.entry",
|
|
634
|
+
KANBAN_ENTRY_FILE,
|
|
635
|
+
false
|
|
636
|
+
),
|
|
637
|
+
dataSources: parseKanbanDataSources(rawConfig?.["dataSources"])
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
function createKanbanArtifactEntryPath(config) {
|
|
641
|
+
return config.artifactDir === KANBAN_ARTIFACT_DIR ? config.entry : `${config.artifactDir}/${config.entry}`;
|
|
642
|
+
}
|
|
643
|
+
function readKanbanRelativePath(value, field, fallback, allowDot) {
|
|
644
|
+
const raw = value === void 0 ? fallback : value;
|
|
645
|
+
if (typeof raw !== "string" || raw.trim() === "") {
|
|
646
|
+
throw new Error(`package.json ${field} must be a non-empty string.`);
|
|
647
|
+
}
|
|
648
|
+
const normalized = raw.trim().replaceAll("\\", "/").replace(/\/+$/, "");
|
|
649
|
+
if (allowDot && normalized === ".") return normalized;
|
|
650
|
+
if (normalized === "" || normalized === "." || normalized.startsWith("/") || normalized.startsWith("//") || /^[a-zA-Z]:/.test(normalized) || normalized.includes("\0") || normalized.split("/").some((part) => part === "" || part === "." || part === "..")) {
|
|
651
|
+
throw new Error(`package.json ${field} must be a safe project-relative path.`);
|
|
652
|
+
}
|
|
653
|
+
const reservedDirectory = normalized.split("/").find((part) => [".git", ".hg", ".svn", "node_modules"].includes(part.toLowerCase()));
|
|
654
|
+
if (reservedDirectory !== void 0) {
|
|
655
|
+
throw new Error(
|
|
656
|
+
`package.json ${field} must not use reserved directory ${JSON.stringify(reservedDirectory)}.`
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
if (!allowDot && !/\.html?$/i.test(normalized)) {
|
|
660
|
+
throw new Error(`package.json ${field} must point to an HTML file.`);
|
|
661
|
+
}
|
|
662
|
+
return normalized;
|
|
663
|
+
}
|
|
664
|
+
function parseKanbanDataSources(value) {
|
|
665
|
+
if (value === void 0) return [];
|
|
666
|
+
if (!Array.isArray(value)) {
|
|
667
|
+
throw new Error("package.json workflowCode.kanban.dataSources must be an array.");
|
|
668
|
+
}
|
|
669
|
+
if (value.length > MAX_KANBAN_DATA_SOURCES) {
|
|
670
|
+
throw new Error(`package.json workflowCode.kanban.dataSources supports at most ${MAX_KANBAN_DATA_SOURCES} items.`);
|
|
671
|
+
}
|
|
672
|
+
const ids = /* @__PURE__ */ new Set();
|
|
673
|
+
return value.map((item, index) => {
|
|
674
|
+
if (!isRecord2(item)) {
|
|
675
|
+
throw new Error(`package.json workflowCode.kanban.dataSources[${index}] must be an object.`);
|
|
676
|
+
}
|
|
677
|
+
const supportedFields = /* @__PURE__ */ new Set(["id", "kind", "projectId", "keyPrefixes"]);
|
|
678
|
+
if (Object.keys(item).some((field) => !supportedFields.has(field))) {
|
|
679
|
+
throw new Error(`package.json workflowCode.kanban.dataSources[${index}] contains unsupported fields.`);
|
|
680
|
+
}
|
|
681
|
+
const id = readNonEmptyString(item["id"], `workflowCode.kanban.dataSources[${index}].id`);
|
|
682
|
+
if (!KANBAN_DATA_SOURCE_ID_PATTERN.test(id)) {
|
|
683
|
+
throw new Error(`package.json workflowCode.kanban.dataSources[${index}].id is invalid.`);
|
|
684
|
+
}
|
|
685
|
+
if (ids.has(id)) {
|
|
686
|
+
throw new Error(`package.json workflowCode.kanban.dataSources contains duplicate id "${id}".`);
|
|
687
|
+
}
|
|
688
|
+
ids.add(id);
|
|
689
|
+
if (item["kind"] !== "workflow-kv") {
|
|
690
|
+
throw new Error(`package.json workflowCode.kanban.dataSources[${index}].kind is unsupported.`);
|
|
691
|
+
}
|
|
692
|
+
const projectId = readNonEmptyString(
|
|
693
|
+
item["projectId"],
|
|
694
|
+
`workflowCode.kanban.dataSources[${index}].projectId`
|
|
695
|
+
);
|
|
696
|
+
if (!KANBAN_WORKFLOW_UUID_PATTERN.test(projectId)) {
|
|
697
|
+
throw new Error(`package.json workflowCode.kanban.dataSources[${index}].projectId must be a UUID.`);
|
|
698
|
+
}
|
|
699
|
+
const rawPrefixes = item["keyPrefixes"];
|
|
700
|
+
if (!Array.isArray(rawPrefixes) || rawPrefixes.length === 0 || rawPrefixes.length > MAX_KANBAN_DATA_SOURCE_PREFIXES) {
|
|
701
|
+
throw new Error(
|
|
702
|
+
`package.json workflowCode.kanban.dataSources[${index}].keyPrefixes must contain 1 to ${MAX_KANBAN_DATA_SOURCE_PREFIXES} items.`
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
const keyPrefixes = rawPrefixes.map((prefix, prefixIndex) => {
|
|
706
|
+
const normalized = readNonEmptyString(
|
|
707
|
+
prefix,
|
|
708
|
+
`workflowCode.kanban.dataSources[${index}].keyPrefixes[${prefixIndex}]`
|
|
709
|
+
);
|
|
710
|
+
if (normalized.length > MAX_KANBAN_DATA_SOURCE_KEY_LENGTH || normalized.includes("\0")) {
|
|
711
|
+
throw new Error(`package.json workflowCode.kanban.dataSources[${index}].keyPrefixes[${prefixIndex}] is invalid.`);
|
|
712
|
+
}
|
|
713
|
+
return normalized;
|
|
714
|
+
});
|
|
715
|
+
if (new Set(keyPrefixes).size !== keyPrefixes.length) {
|
|
716
|
+
throw new Error(`package.json workflowCode.kanban.dataSources[${index}].keyPrefixes contains duplicates.`);
|
|
717
|
+
}
|
|
718
|
+
return { id, kind: "workflow-kv", projectId, keyPrefixes };
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
function readNonEmptyString(value, field) {
|
|
722
|
+
if (typeof value !== "string" || value.trim() === "") throw new Error(`kanban.json ${field} must be a non-empty string.`);
|
|
723
|
+
return value.trim();
|
|
724
|
+
}
|
|
725
|
+
function isRecord2(value) {
|
|
726
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// ../../shared/project-upload/index.ts
|
|
730
|
+
var WORKFLOW_IGNORE_FILE = ".workflowignore";
|
|
731
|
+
function hasWorkflowIgnoreRules(content) {
|
|
732
|
+
if (content === void 0) return false;
|
|
733
|
+
return content.split(/\r?\n/).some((line) => {
|
|
734
|
+
const trimmed = line.trim();
|
|
735
|
+
return trimmed !== "" && !trimmed.startsWith("#");
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
function resolveWorkflowUploadRequiredPaths(packageJson, availablePaths) {
|
|
739
|
+
const normalizedAvailablePaths = new Set(
|
|
740
|
+
Array.from(availablePaths, (filePath) => normalizeWorkflowUploadPath(filePath))
|
|
741
|
+
);
|
|
742
|
+
if (readPackageProjectType(packageJson) === "kanban") {
|
|
743
|
+
const artifact = readKanbanArtifactConfig(packageJson);
|
|
744
|
+
return [
|
|
745
|
+
"package.json",
|
|
746
|
+
createKanbanArtifactEntryPath(artifact),
|
|
747
|
+
...normalizedAvailablePaths.has(KANBAN_MANIFEST_FILE) ? [KANBAN_MANIFEST_FILE] : []
|
|
748
|
+
];
|
|
749
|
+
}
|
|
750
|
+
return ["package.json", "index.ts"];
|
|
751
|
+
}
|
|
752
|
+
function createWorkflowUploadMatcher(content) {
|
|
753
|
+
const ruleLines = content === void 0 ? [] : content.split(/\r?\n/);
|
|
754
|
+
const matcher = (0, import_ignore.default)().add(ruleLines);
|
|
755
|
+
return {
|
|
756
|
+
ignores(relativePath, options) {
|
|
757
|
+
const normalized = normalizeWorkflowUploadPath(relativePath, options);
|
|
758
|
+
return normalized === WORKFLOW_IGNORE_FILE ? false : matcher.ignores(normalized);
|
|
759
|
+
},
|
|
760
|
+
explain(relativePath, options) {
|
|
761
|
+
const normalized = normalizeWorkflowUploadPath(relativePath, options);
|
|
762
|
+
if (normalized === WORKFLOW_IGNORE_FILE) return void 0;
|
|
763
|
+
return findEffectiveWorkflowIgnoreRule(normalized, ruleLines);
|
|
764
|
+
},
|
|
765
|
+
assertRequiredPaths(relativePaths) {
|
|
766
|
+
for (const relativePath of relativePaths) {
|
|
767
|
+
const normalized = normalizeWorkflowUploadPath(relativePath);
|
|
768
|
+
if (!matcher.ignores(normalized)) continue;
|
|
769
|
+
const rule = findEffectiveWorkflowIgnoreRule(normalized, ruleLines);
|
|
770
|
+
const suffix = rule === void 0 ? "" : ` by rule ${JSON.stringify(rule)}`;
|
|
771
|
+
throw new Error(
|
|
772
|
+
`Required upload path ${JSON.stringify(normalized)} is excluded by ${WORKFLOW_IGNORE_FILE}${suffix}.`
|
|
773
|
+
);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
function normalizeWorkflowUploadPath(relativePath, options = {}) {
|
|
779
|
+
const normalized = relativePath.replaceAll("\\", "/").replace(/^\.\/+/, "");
|
|
780
|
+
const withoutTrailingSlash = normalized.replace(/\/+$/, "");
|
|
781
|
+
if (withoutTrailingSlash === "" || withoutTrailingSlash.startsWith("/") || /^[a-zA-Z]:/.test(withoutTrailingSlash) || withoutTrailingSlash.includes("\0") || withoutTrailingSlash.split("/").some((part) => part === "" || part === "." || part === "..")) {
|
|
782
|
+
throw new Error(`Invalid workflow upload path ${JSON.stringify(relativePath)}.`);
|
|
783
|
+
}
|
|
784
|
+
return options.directory === true ? `${withoutTrailingSlash}/` : withoutTrailingSlash;
|
|
785
|
+
}
|
|
786
|
+
function createWorkflowUploadPortablePathKey(relativePath) {
|
|
787
|
+
return normalizeWorkflowUploadPath(relativePath).normalize("NFC").toLowerCase();
|
|
788
|
+
}
|
|
789
|
+
function findEffectiveWorkflowIgnoreRule(normalizedPath, ruleLines) {
|
|
790
|
+
const matcher = (0, import_ignore.default)();
|
|
791
|
+
let ignored = false;
|
|
792
|
+
let effectiveRule;
|
|
793
|
+
for (const ruleLine of ruleLines) {
|
|
794
|
+
matcher.add(ruleLine);
|
|
795
|
+
const nextIgnored = matcher.ignores(normalizedPath);
|
|
796
|
+
if (nextIgnored !== ignored) {
|
|
797
|
+
ignored = nextIgnored;
|
|
798
|
+
effectiveRule = ruleLine;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
return ignored ? effectiveRule : void 0;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// ../../shared/project-upload/node.ts
|
|
805
|
+
async function createWorkflowUploadPlan(rootDir) {
|
|
806
|
+
const projectRoot = path2.resolve(rootDir);
|
|
807
|
+
const workflowIgnorePath = path2.join(projectRoot, WORKFLOW_IGNORE_FILE);
|
|
808
|
+
const workflowIgnoreStat = await lstat(workflowIgnorePath).catch(() => void 0);
|
|
809
|
+
if (workflowIgnoreStat?.isSymbolicLink()) {
|
|
810
|
+
throw new Error(`${WORKFLOW_IGNORE_FILE} must be a regular file.`);
|
|
811
|
+
}
|
|
812
|
+
const workflowIgnorePresent = workflowIgnoreStat?.isFile() === true;
|
|
813
|
+
if (workflowIgnoreStat !== void 0 && !workflowIgnorePresent) {
|
|
814
|
+
throw new Error(`${WORKFLOW_IGNORE_FILE} must be a regular file.`);
|
|
815
|
+
}
|
|
816
|
+
const workflowIgnoreContent = workflowIgnorePresent ? await readWorkflowUploadUtf8File(workflowIgnorePath, WORKFLOW_IGNORE_FILE) : void 0;
|
|
817
|
+
const matcher = createWorkflowUploadMatcher(
|
|
818
|
+
workflowIgnoreContent
|
|
819
|
+
);
|
|
820
|
+
const files = [];
|
|
821
|
+
const availableRegularPaths = /* @__PURE__ */ new Set();
|
|
822
|
+
const portablePaths = /* @__PURE__ */ new Map();
|
|
823
|
+
const ignoredPaths = [];
|
|
824
|
+
const walk = async (currentDir) => {
|
|
825
|
+
const entries = await readdir(currentDir, { withFileTypes: true });
|
|
826
|
+
for (const entry of entries) {
|
|
827
|
+
const absolutePath = path2.join(currentDir, entry.name);
|
|
828
|
+
const relativePath = normalizeWorkflowUploadPath(path2.relative(projectRoot, absolutePath));
|
|
829
|
+
const portablePath = createWorkflowUploadPortablePathKey(relativePath);
|
|
830
|
+
const conflictingPath = portablePaths.get(portablePath);
|
|
831
|
+
if (conflictingPath !== void 0 && conflictingPath !== relativePath) {
|
|
832
|
+
throw new Error(
|
|
833
|
+
`Workflow upload paths ${JSON.stringify(conflictingPath)} and ${JSON.stringify(relativePath)} conflict on a case-insensitive filesystem.`
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
portablePaths.set(portablePath, relativePath);
|
|
837
|
+
if (entry.isDirectory()) {
|
|
838
|
+
if (matcher.ignores(relativePath, { directory: true })) {
|
|
839
|
+
ignoredPaths.push(`${relativePath}/`);
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
await walk(absolutePath);
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
if (!entry.isFile()) {
|
|
846
|
+
throw new Error(`Workflow upload path ${JSON.stringify(relativePath)} must be a regular file or directory.`);
|
|
847
|
+
}
|
|
848
|
+
availableRegularPaths.add(relativePath);
|
|
849
|
+
if (matcher.ignores(relativePath)) {
|
|
850
|
+
ignoredPaths.push(relativePath);
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
const fileStat = await lstat(absolutePath);
|
|
854
|
+
files.push({ path: relativePath, absolutePath, size: fileStat.size });
|
|
855
|
+
}
|
|
856
|
+
};
|
|
857
|
+
await walk(projectRoot);
|
|
858
|
+
files.sort((left, right) => left.path.localeCompare(right.path));
|
|
859
|
+
ignoredPaths.sort((left, right) => left.localeCompare(right));
|
|
860
|
+
if (!availableRegularPaths.has("package.json")) {
|
|
861
|
+
throw new Error("Workflow package must contain package.json.");
|
|
862
|
+
}
|
|
863
|
+
const packageJson = parsePackageJson(await readWorkflowUploadUtf8File(
|
|
864
|
+
path2.join(projectRoot, "package.json"),
|
|
865
|
+
"package.json"
|
|
866
|
+
));
|
|
867
|
+
const requiredPaths = resolveWorkflowUploadRequiredPaths(
|
|
868
|
+
packageJson,
|
|
869
|
+
availableRegularPaths
|
|
870
|
+
);
|
|
871
|
+
matcher.assertRequiredPaths(requiredPaths);
|
|
872
|
+
for (const requiredPath of requiredPaths) {
|
|
873
|
+
if (!availableRegularPaths.has(requiredPath)) {
|
|
874
|
+
throw new Error(`Workflow package must contain required upload path ${JSON.stringify(requiredPath)}.`);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
return {
|
|
878
|
+
workflowIgnorePresent,
|
|
879
|
+
workflowIgnoreConfigured: hasWorkflowIgnoreRules(workflowIgnoreContent),
|
|
880
|
+
files,
|
|
881
|
+
ignoredPaths,
|
|
882
|
+
totalBytes: files.reduce((total, file) => total + file.size, 0),
|
|
883
|
+
requiredPaths
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
async function copyWorkflowUploadFiles(sourceDir, targetDir) {
|
|
887
|
+
const plan = await createWorkflowUploadPlan(sourceDir);
|
|
888
|
+
await mkdir(targetDir, { recursive: true });
|
|
889
|
+
for (const file of plan.files) {
|
|
890
|
+
const targetPath = path2.join(targetDir, ...file.path.split("/"));
|
|
891
|
+
await mkdir(path2.dirname(targetPath), { recursive: true });
|
|
892
|
+
await copyFile(file.absolutePath, targetPath);
|
|
893
|
+
}
|
|
894
|
+
return plan;
|
|
895
|
+
}
|
|
896
|
+
function parsePackageJson(content) {
|
|
897
|
+
try {
|
|
898
|
+
return JSON.parse(content);
|
|
899
|
+
} catch {
|
|
900
|
+
throw new Error("Workflow package.json is invalid JSON.");
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
async function readWorkflowUploadUtf8File(filePath, label) {
|
|
904
|
+
const content = await readFile(filePath);
|
|
905
|
+
try {
|
|
906
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
907
|
+
} catch {
|
|
908
|
+
throw new Error(`Workflow ${label} must be a UTF-8 text file.`);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// src/workspace.ts
|
|
913
|
+
import {
|
|
914
|
+
createWorkflowProjectDependencyLocks,
|
|
915
|
+
inspectLocalWorkflowStructure,
|
|
916
|
+
isProjectGroupHostDataPath,
|
|
917
|
+
parseWorkflowProjectDependencyLocks,
|
|
918
|
+
readKanbanArtifactConfig as readKanbanArtifactConfig2
|
|
919
|
+
} from "workflow-code";
|
|
920
|
+
|
|
921
|
+
// src/auth-config.ts
|
|
922
|
+
import { readFileSync } from "fs";
|
|
923
|
+
import { mkdir as mkdir2, readFile as readFile2, rm, writeFile } from "fs/promises";
|
|
924
|
+
async function readCliAuthConfig(env = process.env) {
|
|
925
|
+
try {
|
|
926
|
+
const raw = await readFile2(resolveWorkflowCliAuthFilePath(env), "utf8");
|
|
927
|
+
return normalizeWorkflowAuthConfig(JSON.parse(raw));
|
|
928
|
+
} catch {
|
|
929
|
+
return createEmptyWorkflowAuthConfig();
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
function readCliAuthConfigSync(env = process.env) {
|
|
933
|
+
try {
|
|
934
|
+
const raw = readFileSync(resolveWorkflowCliAuthFilePath(env), "utf8");
|
|
935
|
+
return normalizeWorkflowAuthConfig(JSON.parse(raw));
|
|
936
|
+
} catch {
|
|
937
|
+
return createEmptyWorkflowAuthConfig();
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
async function writeCliAuthConfig(config, env = process.env) {
|
|
941
|
+
await mkdir2(resolveWorkflowCliAuthDir(env), { recursive: true });
|
|
942
|
+
await writeFile(
|
|
943
|
+
resolveWorkflowCliAuthFilePath(env),
|
|
944
|
+
`${JSON.stringify(normalizeWorkflowAuthConfig(config), null, 2)}
|
|
945
|
+
`,
|
|
946
|
+
"utf8"
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
async function clearCliAuthConfig(env = process.env) {
|
|
950
|
+
await rm(resolveWorkflowCliAuthFilePath(env), { force: true });
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// src/env.ts
|
|
954
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
955
|
+
import path3 from "path";
|
|
956
|
+
import { parse } from "dotenv";
|
|
957
|
+
function loadCliEnv() {
|
|
958
|
+
const cliRoot = process.cwd();
|
|
959
|
+
const defaultRepoRoot = findRepoRoot(cliRoot) ?? cliRoot;
|
|
960
|
+
const initialRepoRoot = process.env.WORKFLOW_REPO_ROOT ? path3.resolve(process.env.WORKFLOW_REPO_ROOT) : defaultRepoRoot;
|
|
961
|
+
loadEnvFiles([
|
|
962
|
+
path3.join(cliRoot, ".env"),
|
|
963
|
+
path3.join(initialRepoRoot, ".env")
|
|
964
|
+
]);
|
|
965
|
+
const resolvedRepoRoot = process.env.WORKFLOW_REPO_ROOT ? path3.resolve(process.env.WORKFLOW_REPO_ROOT) : initialRepoRoot;
|
|
966
|
+
if (resolvedRepoRoot !== initialRepoRoot) {
|
|
967
|
+
loadEnvFiles([path3.join(resolvedRepoRoot, ".env")]);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
function findRepoRoot(startDir) {
|
|
971
|
+
let current = path3.resolve(startDir);
|
|
972
|
+
while (true) {
|
|
973
|
+
if (existsSync(path3.join(current, "pnpm-workspace.yaml")) && existsSync(path3.join(current, "packages", "cli", "package.json"))) {
|
|
974
|
+
return current;
|
|
975
|
+
}
|
|
976
|
+
const parent = path3.dirname(current);
|
|
977
|
+
if (parent === current) return void 0;
|
|
978
|
+
current = parent;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
function loadEnvFiles(filePaths) {
|
|
982
|
+
for (const filePath of unique(filePaths)) {
|
|
983
|
+
if (!existsSync(filePath)) {
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
const values = parse(readFileSync2(filePath));
|
|
987
|
+
for (const [key, value] of Object.entries(values)) {
|
|
988
|
+
if (process.env[key] === void 0) {
|
|
989
|
+
process.env[key] = value;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
function unique(values) {
|
|
995
|
+
return [...new Set(values)];
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// src/workspace.ts
|
|
999
|
+
var DEFAULT_SERVER_URL = "http://localhost:7125";
|
|
1000
|
+
var CLI_MODULE_DIR = path4.dirname(fileURLToPath(import.meta.url));
|
|
1001
|
+
var DEFAULT_DEVICE_FLOW_CLIENT_NAME = "Workflow Workspace CLI";
|
|
1002
|
+
loadCliEnv();
|
|
1003
|
+
async function main(argv = process.argv.slice(2), options = {}) {
|
|
1004
|
+
if (options.localeInitialized !== true) {
|
|
1005
|
+
argv = prepareCliInvocation(argv).argv;
|
|
1006
|
+
}
|
|
1007
|
+
const parsed = parseArgs(argv);
|
|
1008
|
+
switch (parsed.command) {
|
|
1009
|
+
case "login":
|
|
1010
|
+
await commandLogin(parsed);
|
|
1011
|
+
return;
|
|
1012
|
+
case "logout":
|
|
1013
|
+
await commandLogout();
|
|
1014
|
+
return;
|
|
1015
|
+
case "status":
|
|
1016
|
+
await commandStatus();
|
|
1017
|
+
return;
|
|
1018
|
+
case "pack":
|
|
1019
|
+
await commandPack(parsed);
|
|
1020
|
+
return;
|
|
1021
|
+
case "upload":
|
|
1022
|
+
await commandUpload(parsed);
|
|
1023
|
+
return;
|
|
1024
|
+
case "preparation":
|
|
1025
|
+
await commandPreparation(parsed);
|
|
1026
|
+
return;
|
|
1027
|
+
case "run":
|
|
1028
|
+
await commandRun(parsed);
|
|
1029
|
+
return;
|
|
1030
|
+
case "debug-node":
|
|
1031
|
+
await commandDebugNode(parsed);
|
|
1032
|
+
return;
|
|
1033
|
+
case "download":
|
|
1034
|
+
await commandDownload(parsed);
|
|
1035
|
+
return;
|
|
1036
|
+
case "publish":
|
|
1037
|
+
await commandPublish(parsed);
|
|
1038
|
+
return;
|
|
1039
|
+
case "versions":
|
|
1040
|
+
await commandVersions(parsed);
|
|
1041
|
+
return;
|
|
1042
|
+
case "health":
|
|
1043
|
+
await commandHealth(parsed);
|
|
1044
|
+
return;
|
|
1045
|
+
case "":
|
|
1046
|
+
case "help":
|
|
1047
|
+
case "--help":
|
|
1048
|
+
case "-h":
|
|
1049
|
+
printUsage();
|
|
1050
|
+
return;
|
|
1051
|
+
default:
|
|
1052
|
+
throw new Error(i18n.t("unknownWorkspaceCommand", { ns: "cli", command: parsed.command }));
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
async function commandLogin(parsed) {
|
|
1056
|
+
const server = parsed.options.server.trim();
|
|
1057
|
+
if (server === "") {
|
|
1058
|
+
throw new Error(i18n.t("loginRequiresServer", { ns: "cli" }));
|
|
1059
|
+
}
|
|
1060
|
+
const started = await fetchApi({
|
|
1061
|
+
server,
|
|
1062
|
+
token: "",
|
|
1063
|
+
path: "/api/auth/device/start",
|
|
1064
|
+
method: "POST",
|
|
1065
|
+
body: {
|
|
1066
|
+
// issue #93 phase 1 moves workspace login to device flow and stores a
|
|
1067
|
+
// dedicated CLI auth file instead of sharing the App config.
|
|
1068
|
+
clientName: DEFAULT_DEVICE_FLOW_CLIENT_NAME,
|
|
1069
|
+
source: "cli"
|
|
1070
|
+
},
|
|
1071
|
+
auth: false
|
|
1072
|
+
});
|
|
1073
|
+
if (started.errCode !== 0 || started.data === void 0) {
|
|
1074
|
+
printApiResponse(started);
|
|
1075
|
+
assertApiOk(started);
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
const verificationUrl = started.data.verificationUriComplete || started.data.verificationUri;
|
|
1079
|
+
void openExternalBrowser(verificationUrl);
|
|
1080
|
+
console.error(i18n.t("deviceLoginPrompt", {
|
|
1081
|
+
ns: "cli",
|
|
1082
|
+
url: verificationUrl,
|
|
1083
|
+
code: started.data.userCode
|
|
1084
|
+
}));
|
|
1085
|
+
const completed = await waitForDeviceToken({
|
|
1086
|
+
server,
|
|
1087
|
+
deviceCode: started.data.deviceCode,
|
|
1088
|
+
intervalSeconds: started.data.intervalSeconds,
|
|
1089
|
+
expiresAt: started.data.expiresAt
|
|
1090
|
+
});
|
|
1091
|
+
if (completed.errCode !== 0 || completed.data === void 0) {
|
|
1092
|
+
printApiResponse(completed);
|
|
1093
|
+
assertApiOk(completed);
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
await writeCliAuthConfig({
|
|
1097
|
+
serverUrl: server,
|
|
1098
|
+
apiKey: completed.data.accessToken,
|
|
1099
|
+
expiresAt: completed.data.expiresAt ?? completed.data.apiKey.expiresAt,
|
|
1100
|
+
user: completed.data.user,
|
|
1101
|
+
source: "device_flow"
|
|
1102
|
+
});
|
|
1103
|
+
printApiResponse({
|
|
1104
|
+
errCode: 0,
|
|
1105
|
+
errMessage: "",
|
|
1106
|
+
data: {
|
|
1107
|
+
loggedIn: true,
|
|
1108
|
+
serverUrl: server,
|
|
1109
|
+
user: completed.data.user,
|
|
1110
|
+
expiresAt: completed.data.expiresAt ?? completed.data.apiKey.expiresAt,
|
|
1111
|
+
apiKeyPrefix: completed.data.apiKey.prefix
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
async function commandLogout() {
|
|
1116
|
+
await clearCliAuthConfig();
|
|
1117
|
+
printApiResponse({ errCode: 0, errMessage: "", data: { loggedIn: false } });
|
|
1118
|
+
}
|
|
1119
|
+
async function commandStatus() {
|
|
1120
|
+
const authConfig = await readCliAuthConfig();
|
|
1121
|
+
printApiResponse({
|
|
1122
|
+
errCode: 0,
|
|
1123
|
+
errMessage: "",
|
|
1124
|
+
data: {
|
|
1125
|
+
loggedIn: authConfig.serverUrl.trim() !== "" && authConfig.apiKey.trim() !== "",
|
|
1126
|
+
serverUrl: authConfig.serverUrl,
|
|
1127
|
+
user: authConfig.user,
|
|
1128
|
+
expiresAt: authConfig.expiresAt,
|
|
1129
|
+
expiringSoon: isWorkflowAuthExpiringSoon(authConfig.expiresAt),
|
|
1130
|
+
source: authConfig.source ?? null
|
|
1131
|
+
}
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
async function commandPack(parsed) {
|
|
1135
|
+
const workflowName = requirePositional(parsed, 0, "workflow");
|
|
1136
|
+
if (parsed.options.projectGroup === true || parsed.options.publishGroup === true || parsed.options.dependencies.length > 0 || parsed.options.dataGrants.length > 0) {
|
|
1137
|
+
const packed = await packProjectGroup({
|
|
1138
|
+
workflowArg: workflowName,
|
|
1139
|
+
sourcePath: parsed.options.path,
|
|
1140
|
+
outputPath: parsed.options.output,
|
|
1141
|
+
dependencyOptions: parsed.options.dependencies
|
|
1142
|
+
});
|
|
1143
|
+
console.log(JSON.stringify({ archivePath: packed.archivePath, manifest: packed.manifest }, null, 2));
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
const archivePath = await packWorkflow(workflowName, parsed.options.output, parsed.options.path);
|
|
1147
|
+
console.log(JSON.stringify({ archivePath }, null, 2));
|
|
1148
|
+
}
|
|
1149
|
+
async function commandUpload(parsed) {
|
|
1150
|
+
requireLogin(parsed);
|
|
1151
|
+
const workflowArg = requirePositional(parsed, 0, "workflow");
|
|
1152
|
+
if (shouldUseProjectGroupUpload(parsed.options)) {
|
|
1153
|
+
await commandProjectGroupUpload(parsed, workflowArg);
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
const packageContext = await readWorkflowPackageContext(workflowArg, parsed.options.path);
|
|
1157
|
+
let workflowId = packageContext.workflowId;
|
|
1158
|
+
if (parsed.options.create === true) {
|
|
1159
|
+
const created = await ensureWorkflowProjectCreated({
|
|
1160
|
+
packageContext,
|
|
1161
|
+
server: parsed.options.server,
|
|
1162
|
+
token: parsed.options.token
|
|
1163
|
+
});
|
|
1164
|
+
workflowId = created.workflowId;
|
|
1165
|
+
}
|
|
1166
|
+
if (!workflowId) {
|
|
1167
|
+
throw new Error(i18n.t("workflowIdRequired", { ns: "cli" }));
|
|
1168
|
+
}
|
|
1169
|
+
const archivePath = parsed.options.file ? path4.resolve(parsed.options.file) : await packWorkflow(workflowArg, parsed.options.output, parsed.options.path);
|
|
1170
|
+
const uploadResult = await uploadPackage({
|
|
1171
|
+
workflowId,
|
|
1172
|
+
archivePath,
|
|
1173
|
+
server: parsed.options.server,
|
|
1174
|
+
token: parsed.options.token
|
|
1175
|
+
});
|
|
1176
|
+
if (uploadResult.errCode !== 0 || uploadResult.data === void 0) {
|
|
1177
|
+
printApiResponse(uploadResult);
|
|
1178
|
+
assertApiOk(uploadResult);
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
if (parsed.options.noWait === true) {
|
|
1182
|
+
printApiResponse(uploadResult);
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
const preparationResult = await waitForPreparation({
|
|
1186
|
+
workflowId,
|
|
1187
|
+
jobId: uploadResult.data.jobId,
|
|
1188
|
+
server: parsed.options.server,
|
|
1189
|
+
token: parsed.options.token
|
|
1190
|
+
});
|
|
1191
|
+
printApiResponse(preparationResult);
|
|
1192
|
+
assertApiOk(preparationResult);
|
|
1193
|
+
if (preparationResult.errCode !== 0) {
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
if (parsed.options.releaseLog !== void 0) {
|
|
1197
|
+
const publishResult = await fetchApi({
|
|
1198
|
+
server: parsed.options.server,
|
|
1199
|
+
token: parsed.options.token,
|
|
1200
|
+
path: `/api/workflows/${encodeURIComponent(workflowId)}/publish`,
|
|
1201
|
+
method: "POST",
|
|
1202
|
+
body: {
|
|
1203
|
+
releaseLog: parsed.options.releaseLog,
|
|
1204
|
+
sourceMode: parsed.options.sourceMode ?? "bundled"
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
printApiResponse(publishResult);
|
|
1208
|
+
assertApiOk(publishResult);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
function shouldUseProjectGroupUpload(options) {
|
|
1212
|
+
return options.projectGroup === true || options.publishGroup === true || options.dependencies.length > 0 || options.dataGrants.length > 0;
|
|
1213
|
+
}
|
|
1214
|
+
async function commandProjectGroupUpload(parsed, workflowArg, forcePublish = false) {
|
|
1215
|
+
if (parsed.options.file !== void 0) {
|
|
1216
|
+
throw new Error(i18n.t("projectGroupUploadFileUnsupported", { ns: "cli" }));
|
|
1217
|
+
}
|
|
1218
|
+
let rootContext = await readWorkflowPackageContext(workflowArg, parsed.options.path);
|
|
1219
|
+
if (parsed.options.create === true || rootContext.workflowId !== void 0) {
|
|
1220
|
+
await ensureWorkflowProjectCreated({
|
|
1221
|
+
packageContext: rootContext,
|
|
1222
|
+
server: parsed.options.server,
|
|
1223
|
+
token: parsed.options.token
|
|
1224
|
+
});
|
|
1225
|
+
rootContext = await readWorkflowPackageContext(workflowArg, parsed.options.path);
|
|
1226
|
+
}
|
|
1227
|
+
if (!rootContext.workflowId) {
|
|
1228
|
+
throw new Error(i18n.t("projectGroupUploadRootIdRequired", { ns: "cli" }));
|
|
1229
|
+
}
|
|
1230
|
+
const contexts = await readProjectGroupContexts({
|
|
1231
|
+
workflowArg,
|
|
1232
|
+
sourcePath: parsed.options.path,
|
|
1233
|
+
dependencyOptions: parsed.options.dependencies
|
|
1234
|
+
});
|
|
1235
|
+
for (const dependency of contexts.dependencies) {
|
|
1236
|
+
await ensureWorkflowProjectCreated({
|
|
1237
|
+
packageContext: dependency,
|
|
1238
|
+
server: parsed.options.server,
|
|
1239
|
+
token: parsed.options.token
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
const packed = await packProjectGroup({
|
|
1243
|
+
workflowArg,
|
|
1244
|
+
sourcePath: parsed.options.path,
|
|
1245
|
+
outputPath: parsed.options.output,
|
|
1246
|
+
dependencyOptions: parsed.options.dependencies,
|
|
1247
|
+
contexts
|
|
1248
|
+
});
|
|
1249
|
+
const planned = await fetchApi({
|
|
1250
|
+
server: parsed.options.server,
|
|
1251
|
+
token: parsed.options.token,
|
|
1252
|
+
path: "/api/deployments",
|
|
1253
|
+
method: "POST",
|
|
1254
|
+
body: {
|
|
1255
|
+
manifest: packed.manifest,
|
|
1256
|
+
releaseLog: parsed.options.releaseLog
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
if (planned.errCode !== 0 || planned.data === void 0) {
|
|
1260
|
+
printApiResponse(planned);
|
|
1261
|
+
assertApiOk(planned);
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
const prepared = await uploadProjectGroupPackage({
|
|
1265
|
+
deploymentId: planned.data.id,
|
|
1266
|
+
archivePath: packed.archivePath,
|
|
1267
|
+
server: parsed.options.server,
|
|
1268
|
+
token: parsed.options.token
|
|
1269
|
+
});
|
|
1270
|
+
if (prepared.errCode !== 0 || prepared.data === void 0) {
|
|
1271
|
+
printApiResponse(prepared);
|
|
1272
|
+
assertApiOk(prepared);
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
const grantsByProject = resolveExplicitDataGrants(
|
|
1276
|
+
packed.manifest.dependencies,
|
|
1277
|
+
parsed.options.dataGrants
|
|
1278
|
+
);
|
|
1279
|
+
for (const [sourceProjectId, prefixes] of grantsByProject) {
|
|
1280
|
+
const grant = await fetchApi({
|
|
1281
|
+
server: parsed.options.server,
|
|
1282
|
+
token: parsed.options.token,
|
|
1283
|
+
path: `/api/workflows/${encodeURIComponent(sourceProjectId)}/data-source-grants/${encodeURIComponent(packed.manifest.rootProjectId)}`,
|
|
1284
|
+
method: "PUT",
|
|
1285
|
+
body: { keyPrefixes: prefixes }
|
|
1286
|
+
});
|
|
1287
|
+
if (grant.errCode !== 0) {
|
|
1288
|
+
printApiResponse(grant);
|
|
1289
|
+
assertApiOk(grant);
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
if (!forcePublish && parsed.options.releaseLog === void 0 && parsed.options.publishGroup !== true) {
|
|
1294
|
+
printApiResponse(prepared);
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
const published = await fetchApi({
|
|
1298
|
+
server: parsed.options.server,
|
|
1299
|
+
token: parsed.options.token,
|
|
1300
|
+
path: `/api/deployments/${encodeURIComponent(planned.data.id)}/publish`,
|
|
1301
|
+
method: "POST"
|
|
1302
|
+
});
|
|
1303
|
+
printApiResponse(published);
|
|
1304
|
+
assertApiOk(published);
|
|
1305
|
+
}
|
|
1306
|
+
async function commandPreparation(parsed) {
|
|
1307
|
+
requireLogin(parsed);
|
|
1308
|
+
const workflowId = requirePositional(parsed, 0, "workflow");
|
|
1309
|
+
const jobId = requirePositional(parsed, 1, "jobId");
|
|
1310
|
+
const result = await getPreparation({
|
|
1311
|
+
workflowId,
|
|
1312
|
+
jobId,
|
|
1313
|
+
server: parsed.options.server,
|
|
1314
|
+
token: parsed.options.token
|
|
1315
|
+
});
|
|
1316
|
+
printApiResponse(result);
|
|
1317
|
+
assertApiOk(result);
|
|
1318
|
+
}
|
|
1319
|
+
async function commandRun(parsed) {
|
|
1320
|
+
requireLogin(parsed);
|
|
1321
|
+
const workflowId = await resolveRemoteWorkflowId(parsed);
|
|
1322
|
+
const workflowArgs = parsed.positional.slice(1);
|
|
1323
|
+
const result = await fetchApi({
|
|
1324
|
+
server: parsed.options.server,
|
|
1325
|
+
token: parsed.options.token,
|
|
1326
|
+
path: `/api/workflows/${encodeURIComponent(workflowId)}/run`,
|
|
1327
|
+
method: "POST",
|
|
1328
|
+
body: {
|
|
1329
|
+
target: parsed.options.target ?? "latest",
|
|
1330
|
+
entrypointId: parsed.options.entrypoint,
|
|
1331
|
+
args: workflowArgs
|
|
1332
|
+
}
|
|
1333
|
+
});
|
|
1334
|
+
printApiResponse(result);
|
|
1335
|
+
assertApiOk(result);
|
|
1336
|
+
}
|
|
1337
|
+
async function commandDebugNode(parsed) {
|
|
1338
|
+
requireLogin(parsed);
|
|
1339
|
+
const workflowId = await resolveRemoteWorkflowId(parsed);
|
|
1340
|
+
const nodeName = requirePositional(parsed, 1, "node");
|
|
1341
|
+
const workflowArgs = parsed.positional.slice(2);
|
|
1342
|
+
const result = await fetchApi({
|
|
1343
|
+
server: parsed.options.server,
|
|
1344
|
+
token: parsed.options.token,
|
|
1345
|
+
path: `/api/workflows/${encodeURIComponent(workflowId)}/debug/nodes/${encodeURIComponent(nodeName)}`,
|
|
1346
|
+
method: "POST",
|
|
1347
|
+
body: {
|
|
1348
|
+
target: parsed.options.target ?? "latest",
|
|
1349
|
+
entrypointId: parsed.options.entrypoint,
|
|
1350
|
+
args: workflowArgs
|
|
1351
|
+
}
|
|
1352
|
+
});
|
|
1353
|
+
printApiResponse(result);
|
|
1354
|
+
assertApiOk(result);
|
|
1355
|
+
}
|
|
1356
|
+
async function commandVersions(parsed) {
|
|
1357
|
+
requireLogin(parsed);
|
|
1358
|
+
const workflowId = await resolveRemoteWorkflowId(parsed);
|
|
1359
|
+
const result = await fetchApi({
|
|
1360
|
+
server: parsed.options.server,
|
|
1361
|
+
token: parsed.options.token,
|
|
1362
|
+
path: `/api/workflows/${encodeURIComponent(workflowId)}/versions`
|
|
1363
|
+
});
|
|
1364
|
+
printApiResponse(result);
|
|
1365
|
+
assertApiOk(result);
|
|
1366
|
+
}
|
|
1367
|
+
async function commandDownload(parsed) {
|
|
1368
|
+
requireLogin(parsed);
|
|
1369
|
+
const workflowArg = requirePositional(parsed, 0, "workflow");
|
|
1370
|
+
const workflowId = await resolveRemoteWorkflowId(parsed);
|
|
1371
|
+
const target = parsed.options.target ?? "latest";
|
|
1372
|
+
if (target !== "draft") {
|
|
1373
|
+
const result2 = await fetchApi({
|
|
1374
|
+
server: parsed.options.server,
|
|
1375
|
+
token: parsed.options.token,
|
|
1376
|
+
path: `/api/workflows/${encodeURIComponent(workflowId)}/project-group-download?target=${encodeURIComponent(target)}`
|
|
1377
|
+
});
|
|
1378
|
+
assertApiOk(result2);
|
|
1379
|
+
if (result2.errCode !== 0 || result2.data === void 0) {
|
|
1380
|
+
printApiResponse(result2);
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
const repoRoot2 = resolveRepoRoot();
|
|
1384
|
+
const rootTargetDir = parsed.options.path ? path4.resolve(parsed.options.path) : path4.join(repoRoot2, "workspace", "workflow", workflowArg);
|
|
1385
|
+
const materialized = await materializeProjectGroupDownload(result2.data, rootTargetDir);
|
|
1386
|
+
const locallyAvailableProjectIds = new Set(materialized.map((project) => project.projectId));
|
|
1387
|
+
const skippedDependencies = result2.data.skippedDependencies.filter((item) => item.dependency.delivery !== "included" || !locallyAvailableProjectIds.has(item.dependency.projectId));
|
|
1388
|
+
const complete = !skippedDependencies.some((item) => item.dependency.delivery === "included");
|
|
1389
|
+
printApiResponse({
|
|
1390
|
+
...result2,
|
|
1391
|
+
data: {
|
|
1392
|
+
...result2.data,
|
|
1393
|
+
complete,
|
|
1394
|
+
copyAllowed: complete,
|
|
1395
|
+
skippedDependencies,
|
|
1396
|
+
projects: materialized
|
|
1397
|
+
}
|
|
1398
|
+
});
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
const result = await fetchApi({
|
|
1402
|
+
server: parsed.options.server,
|
|
1403
|
+
token: parsed.options.token,
|
|
1404
|
+
path: `/api/workflows/${encodeURIComponent(workflowId)}/files?target=${encodeURIComponent(target)}&includeBinary=true`
|
|
1405
|
+
});
|
|
1406
|
+
printApiResponse(result);
|
|
1407
|
+
assertApiOk(result);
|
|
1408
|
+
if (result.errCode !== 0 || result.data === void 0) {
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
const repoRoot = resolveRepoRoot();
|
|
1412
|
+
const targetDir = parsed.options.path ? path4.resolve(parsed.options.path) : path4.join(repoRoot, "workspace", "workflow", workflowArg);
|
|
1413
|
+
await rm2(targetDir, { recursive: true, force: true });
|
|
1414
|
+
await mkdir3(targetDir, { recursive: true });
|
|
1415
|
+
for (const file of result.data.files) {
|
|
1416
|
+
const safePath = normalizeWorkflowFilePath(file.path);
|
|
1417
|
+
const targetPath = path4.join(targetDir, safePath);
|
|
1418
|
+
await mkdir3(path4.dirname(targetPath), { recursive: true });
|
|
1419
|
+
await writeFile2(targetPath, decodeWorkflowFileContent(file));
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
async function commandPublish(parsed) {
|
|
1423
|
+
requireLogin(parsed);
|
|
1424
|
+
if (parsed.options.deployment !== void 0) {
|
|
1425
|
+
const publishResult2 = await fetchApi({
|
|
1426
|
+
server: parsed.options.server,
|
|
1427
|
+
token: parsed.options.token,
|
|
1428
|
+
path: `/api/deployments/${encodeURIComponent(parsed.options.deployment)}/publish`,
|
|
1429
|
+
method: "POST"
|
|
1430
|
+
});
|
|
1431
|
+
printApiResponse(publishResult2);
|
|
1432
|
+
assertApiOk(publishResult2);
|
|
1433
|
+
return;
|
|
1434
|
+
}
|
|
1435
|
+
if (shouldUseProjectGroupUpload(parsed.options)) {
|
|
1436
|
+
const workflowArg = requirePositional(parsed, 0, "workflow");
|
|
1437
|
+
await commandProjectGroupUpload(parsed, workflowArg, true);
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
const workflowId = await resolveRemoteWorkflowId(parsed);
|
|
1441
|
+
const publishResult = await fetchApi({
|
|
1442
|
+
server: parsed.options.server,
|
|
1443
|
+
token: parsed.options.token,
|
|
1444
|
+
path: `/api/workflows/${encodeURIComponent(workflowId)}/publish`,
|
|
1445
|
+
method: "POST",
|
|
1446
|
+
body: {
|
|
1447
|
+
releaseLog: parsed.options.releaseLog,
|
|
1448
|
+
sourceMode: parsed.options.sourceMode ?? "bundled"
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
printApiResponse(publishResult);
|
|
1452
|
+
assertApiOk(publishResult);
|
|
1453
|
+
}
|
|
1454
|
+
async function materializeProjectGroupDownload(download, rootTargetDir) {
|
|
1455
|
+
if (!isWorkflowUuid(download.rootProjectId)) {
|
|
1456
|
+
throw new Error(i18n.t("projectGroupDownloadInvalidRootId", { ns: "cli" }));
|
|
1457
|
+
}
|
|
1458
|
+
const dependencies = parseWorkflowProjectDependencyLocks(download.dependencies, { published: true });
|
|
1459
|
+
const includedDependencies = dependencies.filter((dependency) => dependency.delivery === "included");
|
|
1460
|
+
const includedProjectIds = new Set(includedDependencies.map((dependency) => dependency.projectId));
|
|
1461
|
+
const rootProjects = download.projects.filter((project) => project.role === "root");
|
|
1462
|
+
if (rootProjects.length !== 1 || rootProjects[0]?.projectId !== download.rootProjectId) {
|
|
1463
|
+
throw new Error(i18n.t("projectGroupDownloadRootCountInvalid", { ns: "cli" }));
|
|
1464
|
+
}
|
|
1465
|
+
const rootProject = rootProjects[0];
|
|
1466
|
+
if (rootProject.version !== download.rootVersion) {
|
|
1467
|
+
throw new Error(i18n.t("projectGroupDownloadRootVersionMismatch", { ns: "cli" }));
|
|
1468
|
+
}
|
|
1469
|
+
const rootPackageFile = rootProject.files.find((file) => normalizeWorkflowFilePath(file.path) === "package.json");
|
|
1470
|
+
if (rootPackageFile === void 0 || rootPackageFile.encoding === "base64") {
|
|
1471
|
+
throw new Error(i18n.t("projectGroupDownloadRootPackageUtf8", { ns: "cli" }));
|
|
1472
|
+
}
|
|
1473
|
+
let rootPackageJson;
|
|
1474
|
+
try {
|
|
1475
|
+
rootPackageJson = JSON.parse(rootPackageFile.content);
|
|
1476
|
+
} catch {
|
|
1477
|
+
throw new Error(i18n.t("projectGroupDownloadRootPackageInvalidJson", { ns: "cli" }));
|
|
1478
|
+
}
|
|
1479
|
+
const declaredSources = readKanbanArtifactConfig2(rootPackageJson).dataSources;
|
|
1480
|
+
const selfReference = declaredSources.find((source) => source.projectId === download.rootProjectId);
|
|
1481
|
+
if (selfReference !== void 0) {
|
|
1482
|
+
throw new Error(i18n.t("projectGroupDownloadSelfReference", {
|
|
1483
|
+
ns: "cli",
|
|
1484
|
+
sourceId: JSON.stringify(selfReference.id)
|
|
1485
|
+
}));
|
|
1486
|
+
}
|
|
1487
|
+
if (declaredSources.length !== dependencies.length || declaredSources.some((source) => {
|
|
1488
|
+
const dependency = dependencies.find((candidate) => candidate.sourceId === source.id);
|
|
1489
|
+
return dependency === void 0 || dependency.kind !== source.kind || dependency.projectId !== source.projectId || JSON.stringify(dependency.keyPrefixes) !== JSON.stringify(source.keyPrefixes);
|
|
1490
|
+
})) {
|
|
1491
|
+
throw new Error(i18n.t("projectGroupDownloadLockMismatch", { ns: "cli" }));
|
|
1492
|
+
}
|
|
1493
|
+
const responseProjectIds = /* @__PURE__ */ new Set();
|
|
1494
|
+
for (const project of download.projects) {
|
|
1495
|
+
if (!isWorkflowUuid(project.projectId) || responseProjectIds.has(project.projectId)) {
|
|
1496
|
+
throw new Error(i18n.t("projectGroupDownloadInvalidOrDuplicateProjectId", {
|
|
1497
|
+
ns: "cli",
|
|
1498
|
+
projectId: JSON.stringify(project.projectId)
|
|
1499
|
+
}));
|
|
1500
|
+
}
|
|
1501
|
+
responseProjectIds.add(project.projectId);
|
|
1502
|
+
const filePaths = /* @__PURE__ */ new Set();
|
|
1503
|
+
for (const file of project.files) {
|
|
1504
|
+
const safePath = normalizeWorkflowFilePath(file.path);
|
|
1505
|
+
if (isProjectGroupHostDataPath(safePath)) {
|
|
1506
|
+
throw new Error(i18n.t("projectGroupDownloadForbiddenHostData", {
|
|
1507
|
+
ns: "cli",
|
|
1508
|
+
path: JSON.stringify(file.path)
|
|
1509
|
+
}));
|
|
1510
|
+
}
|
|
1511
|
+
const fileKey = createPortableLocalPathKey(safePath);
|
|
1512
|
+
if (filePaths.has(fileKey)) {
|
|
1513
|
+
throw new Error(i18n.t("projectGroupDownloadDuplicateFile", {
|
|
1514
|
+
ns: "cli",
|
|
1515
|
+
path: JSON.stringify(safePath)
|
|
1516
|
+
}));
|
|
1517
|
+
}
|
|
1518
|
+
filePaths.add(fileKey);
|
|
1519
|
+
}
|
|
1520
|
+
const packageFile = project.files.find((file) => normalizeWorkflowFilePath(file.path) === "package.json");
|
|
1521
|
+
if (packageFile === void 0 || packageFile.encoding === "base64") {
|
|
1522
|
+
throw new Error(i18n.t("projectGroupDownloadPackageUtf8", { ns: "cli", role: project.role }));
|
|
1523
|
+
}
|
|
1524
|
+
let packageJson;
|
|
1525
|
+
try {
|
|
1526
|
+
packageJson = JSON.parse(packageFile.content);
|
|
1527
|
+
} catch {
|
|
1528
|
+
throw new Error(i18n.t("projectGroupDownloadPackageInvalidJson", { ns: "cli", role: project.role }));
|
|
1529
|
+
}
|
|
1530
|
+
if (packageJson["id"] !== project.projectId) {
|
|
1531
|
+
throw new Error(i18n.t("projectGroupDownloadedPackageIdMismatch", {
|
|
1532
|
+
ns: "cli",
|
|
1533
|
+
projectId: JSON.stringify(project.projectId)
|
|
1534
|
+
}));
|
|
1535
|
+
}
|
|
1536
|
+
if (hashWorkflowSourceFiles(project.files) !== project.sourceHash) {
|
|
1537
|
+
throw new Error(i18n.t("projectGroupDownloadSourceHashMismatch", {
|
|
1538
|
+
ns: "cli",
|
|
1539
|
+
role: project.role,
|
|
1540
|
+
projectId: JSON.stringify(project.projectId)
|
|
1541
|
+
}));
|
|
1542
|
+
}
|
|
1543
|
+
if (project.role === "root" && project.sourceId !== void 0) {
|
|
1544
|
+
throw new Error(i18n.t("projectGroupDownloadRootSourceIdForbidden", { ns: "cli" }));
|
|
1545
|
+
}
|
|
1546
|
+
if (project.role === "dependency") {
|
|
1547
|
+
const matchingDependencies = includedDependencies.filter((dependency) => dependency.projectId === project.projectId && dependency.version === project.version && dependency.sourceHash === project.sourceHash);
|
|
1548
|
+
if (matchingDependencies.length === 0 || project.sourceId !== void 0 && !matchingDependencies.some((dependency) => dependency.sourceId === project.sourceId)) {
|
|
1549
|
+
throw new Error(i18n.t("projectGroupDownloadUndeclaredDependency", {
|
|
1550
|
+
ns: "cli",
|
|
1551
|
+
projectId: JSON.stringify(project.projectId)
|
|
1552
|
+
}));
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
const parentDir = path4.dirname(rootTargetDir);
|
|
1557
|
+
const existingById = await findSiblingProjectsById(parentDir);
|
|
1558
|
+
const materialized = [];
|
|
1559
|
+
const pending = [];
|
|
1560
|
+
const targetKeys = /* @__PURE__ */ new Set();
|
|
1561
|
+
const orderedProjects = [...download.projects].sort((left, right) => left.role === right.role ? left.projectId.localeCompare(right.projectId) : left.role === "root" ? -1 : 1);
|
|
1562
|
+
for (const project of orderedProjects) {
|
|
1563
|
+
const existingPath = existingById.get(project.projectId);
|
|
1564
|
+
if (existingPath !== void 0) {
|
|
1565
|
+
await assertDownloadedProjectRole(existingPath, project.projectId, project.role);
|
|
1566
|
+
materialized.push({
|
|
1567
|
+
...project.sourceId === void 0 ? {} : { sourceId: project.sourceId },
|
|
1568
|
+
projectId: project.projectId,
|
|
1569
|
+
projectName: project.projectName,
|
|
1570
|
+
role: project.role,
|
|
1571
|
+
path: existingPath,
|
|
1572
|
+
alreadyAvailable: true
|
|
1573
|
+
});
|
|
1574
|
+
continue;
|
|
1575
|
+
}
|
|
1576
|
+
const targetDir = project.role === "root" ? rootTargetDir : path4.join(parentDir, createProjectDirectoryName(project.projectName, project.projectId));
|
|
1577
|
+
if (existsSync2(targetDir)) {
|
|
1578
|
+
throw new Error(i18n.t("projectGroupDownloadDirectoryProjectMismatch", {
|
|
1579
|
+
ns: "cli",
|
|
1580
|
+
projectName: JSON.stringify(project.projectName),
|
|
1581
|
+
path: JSON.stringify(targetDir)
|
|
1582
|
+
}));
|
|
1583
|
+
}
|
|
1584
|
+
const targetKey = createPortableLocalPathKey(path4.resolve(targetDir));
|
|
1585
|
+
if (targetKeys.has(targetKey)) {
|
|
1586
|
+
throw new Error(i18n.t("projectGroupDownloadDirectoryCollision", {
|
|
1587
|
+
ns: "cli",
|
|
1588
|
+
projectName: JSON.stringify(project.projectName),
|
|
1589
|
+
path: JSON.stringify(targetDir)
|
|
1590
|
+
}));
|
|
1591
|
+
}
|
|
1592
|
+
targetKeys.add(targetKey);
|
|
1593
|
+
pending.push({ project, targetDir });
|
|
1594
|
+
}
|
|
1595
|
+
for (const projectId of includedProjectIds) {
|
|
1596
|
+
if (responseProjectIds.has(projectId)) continue;
|
|
1597
|
+
const existingPath = existingById.get(projectId);
|
|
1598
|
+
if (existingPath === void 0) continue;
|
|
1599
|
+
const packageJson = await assertDownloadedProjectRole(existingPath, projectId, "dependency");
|
|
1600
|
+
materialized.push({
|
|
1601
|
+
projectId,
|
|
1602
|
+
projectName: readRequiredPackageString(packageJson, "name"),
|
|
1603
|
+
role: "dependency",
|
|
1604
|
+
path: existingPath,
|
|
1605
|
+
alreadyAvailable: true
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
await mkdir3(parentDir, { recursive: true });
|
|
1609
|
+
const stagingRoot = path4.join(parentDir, `.workflow-download-group-${randomUUID()}`);
|
|
1610
|
+
const staged = [];
|
|
1611
|
+
const moved = [];
|
|
1612
|
+
try {
|
|
1613
|
+
await mkdir3(stagingRoot, { recursive: false });
|
|
1614
|
+
for (const item of pending) {
|
|
1615
|
+
const { project, targetDir } = item;
|
|
1616
|
+
const stagingDir = path4.join(stagingRoot, project.projectId);
|
|
1617
|
+
await mkdir3(stagingDir, { recursive: true });
|
|
1618
|
+
for (const file of project.files) {
|
|
1619
|
+
const safePath = normalizeWorkflowFilePath(file.path);
|
|
1620
|
+
const targetPath = path4.join(stagingDir, ...safePath.split("/"));
|
|
1621
|
+
await mkdir3(path4.dirname(targetPath), { recursive: true });
|
|
1622
|
+
await writeFile2(targetPath, decodeWorkflowFileContent(file));
|
|
1623
|
+
}
|
|
1624
|
+
const packageJson = await readPackageJsonFile(path4.join(stagingDir, "package.json"));
|
|
1625
|
+
if (packageJson["id"] !== project.projectId) {
|
|
1626
|
+
throw new Error(i18n.t("projectGroupDownloadedPackageIdMismatch", {
|
|
1627
|
+
ns: "cli",
|
|
1628
|
+
projectId: JSON.stringify(project.projectId)
|
|
1629
|
+
}));
|
|
1630
|
+
}
|
|
1631
|
+
await assertDownloadedProjectRole(stagingDir, project.projectId, project.role, packageJson);
|
|
1632
|
+
staged.push({ project, targetDir, stagingDir });
|
|
1633
|
+
}
|
|
1634
|
+
for (const item of staged) {
|
|
1635
|
+
await rename(item.stagingDir, item.targetDir);
|
|
1636
|
+
moved.push(item.targetDir);
|
|
1637
|
+
existingById.set(item.project.projectId, item.targetDir);
|
|
1638
|
+
materialized.push({
|
|
1639
|
+
...item.project.sourceId === void 0 ? {} : { sourceId: item.project.sourceId },
|
|
1640
|
+
projectId: item.project.projectId,
|
|
1641
|
+
projectName: item.project.projectName,
|
|
1642
|
+
role: item.project.role,
|
|
1643
|
+
path: item.targetDir,
|
|
1644
|
+
alreadyAvailable: false
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
} catch (error) {
|
|
1648
|
+
for (const targetDir of moved) {
|
|
1649
|
+
await rm2(targetDir, { recursive: true, force: true }).catch(() => void 0);
|
|
1650
|
+
}
|
|
1651
|
+
throw error;
|
|
1652
|
+
} finally {
|
|
1653
|
+
await rm2(stagingRoot, { recursive: true, force: true }).catch(() => void 0);
|
|
1654
|
+
}
|
|
1655
|
+
return materialized.sort((left, right) => left.role === right.role ? left.projectId.localeCompare(right.projectId) : left.role === "root" ? -1 : 1);
|
|
1656
|
+
}
|
|
1657
|
+
async function assertDownloadedProjectRole(projectDir, projectId, role, packageJson) {
|
|
1658
|
+
const metadata = packageJson ?? await readPackageJsonFile(path4.join(projectDir, "package.json"));
|
|
1659
|
+
if (metadata["id"] !== projectId) {
|
|
1660
|
+
throw new Error(i18n.t("projectGroupDownloadedPackageIdMismatch", {
|
|
1661
|
+
ns: "cli",
|
|
1662
|
+
projectId: JSON.stringify(projectId)
|
|
1663
|
+
}));
|
|
1664
|
+
}
|
|
1665
|
+
const projectType = inspectLocalWorkflowStructure({ workflowDir: projectDir }).projectType;
|
|
1666
|
+
const expectedProjectType = role === "root" ? "kanban" : "workflow";
|
|
1667
|
+
if (projectType !== expectedProjectType) {
|
|
1668
|
+
throw new Error(i18n.t("projectGroupDownloadedProjectTypeMismatch", {
|
|
1669
|
+
ns: "cli",
|
|
1670
|
+
projectId: JSON.stringify(projectId),
|
|
1671
|
+
projectType: expectedProjectType
|
|
1672
|
+
}));
|
|
1673
|
+
}
|
|
1674
|
+
return metadata;
|
|
1675
|
+
}
|
|
1676
|
+
function createPortableLocalPathKey(filePath) {
|
|
1677
|
+
return filePath.normalize("NFC").toLowerCase();
|
|
1678
|
+
}
|
|
1679
|
+
function resolveExplicitDataGrants(dependencies, sourceIds) {
|
|
1680
|
+
const selected = /* @__PURE__ */ new Set();
|
|
1681
|
+
for (const sourceId of sourceIds) {
|
|
1682
|
+
if (selected.has(sourceId)) {
|
|
1683
|
+
throw new Error(i18n.t("duplicateGrantDataAccessSource", {
|
|
1684
|
+
ns: "cli",
|
|
1685
|
+
sourceId: JSON.stringify(sourceId)
|
|
1686
|
+
}));
|
|
1687
|
+
}
|
|
1688
|
+
selected.add(sourceId);
|
|
1689
|
+
}
|
|
1690
|
+
const grants = /* @__PURE__ */ new Map();
|
|
1691
|
+
for (const sourceId of selected) {
|
|
1692
|
+
const dependency = dependencies.find((candidate) => candidate.sourceId === sourceId);
|
|
1693
|
+
if (dependency === void 0) {
|
|
1694
|
+
throw new Error(i18n.t("unknownGrantDataAccessSource", {
|
|
1695
|
+
ns: "cli",
|
|
1696
|
+
sourceId: JSON.stringify(sourceId)
|
|
1697
|
+
}));
|
|
1698
|
+
}
|
|
1699
|
+
grants.set(dependency.projectId, [
|
|
1700
|
+
...grants.get(dependency.projectId) ?? [],
|
|
1701
|
+
...dependency.keyPrefixes
|
|
1702
|
+
]);
|
|
1703
|
+
}
|
|
1704
|
+
for (const [projectId, prefixes] of grants) {
|
|
1705
|
+
grants.set(projectId, [...new Set(prefixes)]);
|
|
1706
|
+
}
|
|
1707
|
+
return grants;
|
|
1708
|
+
}
|
|
1709
|
+
async function findSiblingProjectsById(parentDir) {
|
|
1710
|
+
const projects = /* @__PURE__ */ new Map();
|
|
1711
|
+
const entries = await readdir2(parentDir, { withFileTypes: true }).catch(() => []);
|
|
1712
|
+
for (const entry of entries) {
|
|
1713
|
+
if (!entry.isDirectory()) continue;
|
|
1714
|
+
const projectDir = path4.join(parentDir, entry.name);
|
|
1715
|
+
const packageJson = await readPackageJsonFile(path4.join(projectDir, "package.json")).catch(() => void 0);
|
|
1716
|
+
const projectId = packageJson?.["id"];
|
|
1717
|
+
if (typeof projectId === "string" && isWorkflowUuid(projectId) && !projects.has(projectId)) {
|
|
1718
|
+
projects.set(projectId, projectDir);
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
return projects;
|
|
1722
|
+
}
|
|
1723
|
+
function createProjectDirectoryName(projectName, projectId) {
|
|
1724
|
+
const normalized = projectName.normalize("NFC").replace(/^@/, "").replace(/[\\/:*?"<>|\u0000-\u001f]/g, "-").replace(/^\.+|\.+$/g, "").trim();
|
|
1725
|
+
return normalized === "" ? projectId : normalized;
|
|
1726
|
+
}
|
|
1727
|
+
async function commandHealth(parsed) {
|
|
1728
|
+
const result = await fetchApi({
|
|
1729
|
+
server: parsed.options.server,
|
|
1730
|
+
token: "",
|
|
1731
|
+
path: "/health",
|
|
1732
|
+
auth: false
|
|
1733
|
+
});
|
|
1734
|
+
printApiResponse(result);
|
|
1735
|
+
assertApiOk(result);
|
|
1736
|
+
}
|
|
1737
|
+
async function packWorkflow(workflowName, outputPath, sourcePath, options = {}) {
|
|
1738
|
+
const repoRoot = resolveRepoRoot();
|
|
1739
|
+
const workflowDir = sourcePath === void 0 ? path4.join(repoRoot, "workspace", "workflow", workflowName) : path4.resolve(sourcePath);
|
|
1740
|
+
await readPackageJsonFile(path4.join(workflowDir, "package.json"));
|
|
1741
|
+
const packsDir = process.env.WORKFLOW_WORKSPACE_PACKS_DIR ? path4.resolve(process.env.WORKFLOW_WORKSPACE_PACKS_DIR) : path4.join(repoRoot, "workspace", ".packs");
|
|
1742
|
+
const stagingDir = path4.join(packsDir, "tmp", randomUUID(), workflowName);
|
|
1743
|
+
const archivePath = outputPath ? path4.resolve(outputPath) : path4.join(packsDir, `${workflowName}-${createTimestamp()}.tgz`);
|
|
1744
|
+
const workspaceRoot = sourcePath === void 0 ? path4.join(repoRoot, "workspace", "workflow") : path4.dirname(workflowDir);
|
|
1745
|
+
await rm2(path4.dirname(stagingDir), { recursive: true, force: true });
|
|
1746
|
+
await mkdir3(path4.join(stagingDir, "workspace", "workflow"), { recursive: true });
|
|
1747
|
+
await mkdir3(path4.dirname(archivePath), { recursive: true });
|
|
1748
|
+
await stageWorkflowWithLocalDependencies({
|
|
1749
|
+
workspaceRoot,
|
|
1750
|
+
workflowDir,
|
|
1751
|
+
workflowName,
|
|
1752
|
+
stagingRoot: path4.join(stagingDir, "workspace", "workflow"),
|
|
1753
|
+
projectGroupSafe: options.projectGroupSafe === true
|
|
1754
|
+
});
|
|
1755
|
+
const result = await runCommand("tar", ["-czf", archivePath, "-C", stagingDir, "."], {
|
|
1756
|
+
cwd: repoRoot
|
|
1757
|
+
});
|
|
1758
|
+
await rm2(path4.dirname(stagingDir), { recursive: true, force: true });
|
|
1759
|
+
if (result.exitCode !== 0) {
|
|
1760
|
+
throw new Error(i18n.t("archiveCreateFailed", {
|
|
1761
|
+
ns: "cli",
|
|
1762
|
+
details: result.stderr || result.stdout
|
|
1763
|
+
}));
|
|
1764
|
+
}
|
|
1765
|
+
return archivePath;
|
|
1766
|
+
}
|
|
1767
|
+
async function readProjectGroupContexts(options) {
|
|
1768
|
+
const root = await readWorkflowPackageContext(options.workflowArg, options.sourcePath);
|
|
1769
|
+
if (root.projectType !== "kanban") {
|
|
1770
|
+
throw new Error(i18n.t("projectGroupRootMustBeKanban", { ns: "cli" }));
|
|
1771
|
+
}
|
|
1772
|
+
if (!root.workflowId) {
|
|
1773
|
+
throw new Error(i18n.t("projectGroupRootIdRequired", { ns: "cli" }));
|
|
1774
|
+
}
|
|
1775
|
+
const dataSources = readKanbanArtifactConfig2(root.packageJson).dataSources;
|
|
1776
|
+
const selfReference = dataSources.find((source) => source.projectId === root.workflowId);
|
|
1777
|
+
if (selfReference !== void 0) {
|
|
1778
|
+
throw new Error(i18n.t("projectGroupSelfReference", {
|
|
1779
|
+
ns: "cli",
|
|
1780
|
+
sourceId: JSON.stringify(selfReference.id)
|
|
1781
|
+
}));
|
|
1782
|
+
}
|
|
1783
|
+
const dependencyPaths = /* @__PURE__ */ new Map();
|
|
1784
|
+
for (const option of options.dependencyOptions) {
|
|
1785
|
+
const separator = option.indexOf("=");
|
|
1786
|
+
const sourceId = separator < 1 ? "" : option.slice(0, separator).trim();
|
|
1787
|
+
const dependencyPath = separator < 0 ? "" : option.slice(separator + 1).trim();
|
|
1788
|
+
if (sourceId === "" || dependencyPath === "") {
|
|
1789
|
+
throw new Error(i18n.t("invalidDependencyOption", {
|
|
1790
|
+
ns: "cli",
|
|
1791
|
+
option: JSON.stringify(option)
|
|
1792
|
+
}));
|
|
1793
|
+
}
|
|
1794
|
+
if (dependencyPaths.has(sourceId)) {
|
|
1795
|
+
throw new Error(i18n.t("duplicateDependencySource", {
|
|
1796
|
+
ns: "cli",
|
|
1797
|
+
sourceId: JSON.stringify(sourceId)
|
|
1798
|
+
}));
|
|
1799
|
+
}
|
|
1800
|
+
dependencyPaths.set(sourceId, path4.resolve(dependencyPath));
|
|
1801
|
+
}
|
|
1802
|
+
const locks = createWorkflowProjectDependencyLocks(
|
|
1803
|
+
dataSources,
|
|
1804
|
+
[...dependencyPaths.keys()].map((sourceId) => ({ sourceId, delivery: "included" }))
|
|
1805
|
+
);
|
|
1806
|
+
const dependenciesByProject = /* @__PURE__ */ new Map();
|
|
1807
|
+
for (const [sourceId, dependencyPath] of dependencyPaths) {
|
|
1808
|
+
const source = dataSources.find((candidate) => candidate.id === sourceId);
|
|
1809
|
+
const context = await readWorkflowPackageContext(sourceId, dependencyPath);
|
|
1810
|
+
if (context.projectType !== "workflow") {
|
|
1811
|
+
throw new Error(i18n.t("dependencyMustBeWorkflow", {
|
|
1812
|
+
ns: "cli",
|
|
1813
|
+
sourceId: JSON.stringify(sourceId)
|
|
1814
|
+
}));
|
|
1815
|
+
}
|
|
1816
|
+
if (context.workflowId !== source.projectId) {
|
|
1817
|
+
throw new Error(i18n.t("dependencyPackageIdMismatch", {
|
|
1818
|
+
ns: "cli",
|
|
1819
|
+
sourceId: JSON.stringify(sourceId),
|
|
1820
|
+
projectId: JSON.stringify(source.projectId)
|
|
1821
|
+
}));
|
|
1822
|
+
}
|
|
1823
|
+
const existing = dependenciesByProject.get(source.projectId);
|
|
1824
|
+
if (existing !== void 0 && existing.workflowDir !== context.workflowDir) {
|
|
1825
|
+
throw new Error(i18n.t("dependencyProjectPathConflict", {
|
|
1826
|
+
ns: "cli",
|
|
1827
|
+
projectId: JSON.stringify(source.projectId)
|
|
1828
|
+
}));
|
|
1829
|
+
}
|
|
1830
|
+
dependenciesByProject.set(source.projectId, context);
|
|
1831
|
+
}
|
|
1832
|
+
return { root, dependencies: [...dependenciesByProject.values()], locks };
|
|
1833
|
+
}
|
|
1834
|
+
async function packProjectGroup(options) {
|
|
1835
|
+
const contexts = options.contexts ?? await readProjectGroupContexts(options);
|
|
1836
|
+
const repoRoot = resolveRepoRoot();
|
|
1837
|
+
const packsDir = process.env.WORKFLOW_WORKSPACE_PACKS_DIR ? path4.resolve(process.env.WORKFLOW_WORKSPACE_PACKS_DIR) : path4.join(repoRoot, "workspace", ".packs");
|
|
1838
|
+
const groupRoot = path4.join(packsDir, "tmp", `project-group-${randomUUID()}`);
|
|
1839
|
+
const projectsDir = path4.join(groupRoot, "projects");
|
|
1840
|
+
const archivePath = options.outputPath ? path4.resolve(options.outputPath) : path4.join(packsDir, `project-group-${createTimestamp()}.tgz`);
|
|
1841
|
+
await mkdir3(projectsDir, { recursive: true });
|
|
1842
|
+
await mkdir3(path4.dirname(archivePath), { recursive: true });
|
|
1843
|
+
const rootArchivePath = `projects/${contexts.root.workflowId}.tgz`;
|
|
1844
|
+
const projects = [{
|
|
1845
|
+
projectId: contexts.root.workflowId,
|
|
1846
|
+
role: "root",
|
|
1847
|
+
archivePath: rootArchivePath
|
|
1848
|
+
}];
|
|
1849
|
+
try {
|
|
1850
|
+
await packWorkflow(
|
|
1851
|
+
contexts.root.workflowArg,
|
|
1852
|
+
path4.join(groupRoot, ...rootArchivePath.split("/")),
|
|
1853
|
+
contexts.root.workflowDir,
|
|
1854
|
+
{ projectGroupSafe: true }
|
|
1855
|
+
);
|
|
1856
|
+
for (const dependency of contexts.dependencies) {
|
|
1857
|
+
const dependencyArchivePath = `projects/${dependency.workflowId}.tgz`;
|
|
1858
|
+
projects.push({
|
|
1859
|
+
projectId: dependency.workflowId,
|
|
1860
|
+
role: "dependency",
|
|
1861
|
+
archivePath: dependencyArchivePath
|
|
1862
|
+
});
|
|
1863
|
+
await packWorkflow(
|
|
1864
|
+
dependency.workflowArg,
|
|
1865
|
+
path4.join(groupRoot, ...dependencyArchivePath.split("/")),
|
|
1866
|
+
dependency.workflowDir,
|
|
1867
|
+
{ projectGroupSafe: true }
|
|
1868
|
+
);
|
|
1869
|
+
}
|
|
1870
|
+
const manifest = {
|
|
1871
|
+
version: 1,
|
|
1872
|
+
rootProjectId: contexts.root.workflowId,
|
|
1873
|
+
projects,
|
|
1874
|
+
dependencies: contexts.locks
|
|
1875
|
+
};
|
|
1876
|
+
await writeFile2(path4.join(groupRoot, "project-group.json"), `${JSON.stringify(manifest, null, 2)}
|
|
1877
|
+
`);
|
|
1878
|
+
const result = await runCommand("tar", ["-czf", archivePath, "-C", groupRoot, "."], { cwd: repoRoot });
|
|
1879
|
+
if (result.exitCode !== 0) {
|
|
1880
|
+
throw new Error(i18n.t("projectGroupArchiveCreateFailed", {
|
|
1881
|
+
ns: "cli",
|
|
1882
|
+
details: result.stderr || result.stdout
|
|
1883
|
+
}));
|
|
1884
|
+
}
|
|
1885
|
+
return {
|
|
1886
|
+
archivePath,
|
|
1887
|
+
manifest,
|
|
1888
|
+
root: contexts.root,
|
|
1889
|
+
dependencies: contexts.dependencies
|
|
1890
|
+
};
|
|
1891
|
+
} finally {
|
|
1892
|
+
await rm2(groupRoot, { recursive: true, force: true });
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
async function uploadProjectGroupPackage(options) {
|
|
1896
|
+
const archiveBuffer = await readFile3(options.archivePath);
|
|
1897
|
+
const archiveName = path4.basename(options.archivePath);
|
|
1898
|
+
const response = await fetch(
|
|
1899
|
+
`${trimTrailingSlash(options.server)}/api/deployments/${encodeURIComponent(options.deploymentId)}/package?fileName=${encodeURIComponent(archiveName)}`,
|
|
1900
|
+
{
|
|
1901
|
+
method: "POST",
|
|
1902
|
+
headers: {
|
|
1903
|
+
authorization: `Bearer ${options.token}`,
|
|
1904
|
+
"content-type": "application/octet-stream",
|
|
1905
|
+
"x-workflow-package-name": archiveName
|
|
1906
|
+
},
|
|
1907
|
+
body: new Blob([archiveBuffer])
|
|
1908
|
+
}
|
|
1909
|
+
);
|
|
1910
|
+
return response.json();
|
|
1911
|
+
}
|
|
1912
|
+
async function resolveRemoteWorkflowId(parsed) {
|
|
1913
|
+
const workflowArg = requirePositional(parsed, 0, "workflow");
|
|
1914
|
+
if (isWorkflowUuid(workflowArg)) {
|
|
1915
|
+
return workflowArg;
|
|
1916
|
+
}
|
|
1917
|
+
const packageContext = await readWorkflowPackageContext(workflowArg, parsed.options.path);
|
|
1918
|
+
if (packageContext.workflowId) {
|
|
1919
|
+
return packageContext.workflowId;
|
|
1920
|
+
}
|
|
1921
|
+
throw new Error(i18n.t("workflowNotBound", {
|
|
1922
|
+
ns: "cli",
|
|
1923
|
+
workflow: workflowArg
|
|
1924
|
+
}));
|
|
1925
|
+
}
|
|
1926
|
+
async function readWorkflowPackageContext(workflowArg, sourcePath) {
|
|
1927
|
+
const repoRoot = resolveRepoRoot();
|
|
1928
|
+
const workflowDir = sourcePath === void 0 ? path4.join(repoRoot, "workspace", "workflow", workflowArg) : path4.resolve(sourcePath);
|
|
1929
|
+
const packageJsonPath = path4.join(workflowDir, "package.json");
|
|
1930
|
+
const packageJson = await readPackageJsonFile(packageJsonPath);
|
|
1931
|
+
const workflowId = typeof packageJson.id === "string" && isWorkflowUuid(packageJson.id) ? packageJson.id.trim() : void 0;
|
|
1932
|
+
const workflowName = readRequiredPackageString(packageJson, "name");
|
|
1933
|
+
const projectType = inspectLocalWorkflowStructure({ workflowDir }).projectType;
|
|
1934
|
+
return {
|
|
1935
|
+
workflowArg,
|
|
1936
|
+
workflowDir,
|
|
1937
|
+
packageJsonPath,
|
|
1938
|
+
workflowId,
|
|
1939
|
+
workflowName,
|
|
1940
|
+
projectType,
|
|
1941
|
+
packageJson
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
async function ensureWorkflowProjectCreated(options) {
|
|
1945
|
+
const created = await fetchApi({
|
|
1946
|
+
server: options.server,
|
|
1947
|
+
token: options.token,
|
|
1948
|
+
path: "/api/workflows",
|
|
1949
|
+
method: "POST",
|
|
1950
|
+
body: {
|
|
1951
|
+
name: options.packageContext.workflowName,
|
|
1952
|
+
projectType: options.packageContext.projectType,
|
|
1953
|
+
...options.packageContext.workflowId ? { workflowId: options.packageContext.workflowId } : {}
|
|
1954
|
+
}
|
|
1955
|
+
});
|
|
1956
|
+
if (created.errCode !== 0 || created.data === void 0) {
|
|
1957
|
+
printApiResponse(created);
|
|
1958
|
+
assertApiOk(created);
|
|
1959
|
+
throw new Error(i18n.t("projectCreationFailed", { ns: "cli" }));
|
|
1960
|
+
}
|
|
1961
|
+
if (!options.packageContext.workflowId || options.packageContext.workflowId !== created.data.workflowId) {
|
|
1962
|
+
await writeWorkflowPackageId(options.packageContext.packageJsonPath, created.data.workflowId);
|
|
1963
|
+
}
|
|
1964
|
+
return created.data;
|
|
1965
|
+
}
|
|
1966
|
+
async function readPackageJsonFile(packageJsonPath) {
|
|
1967
|
+
try {
|
|
1968
|
+
const raw = JSON.parse(await readFile3(packageJsonPath, "utf8"));
|
|
1969
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
1970
|
+
throw new Error(i18n.t("packageMustBeObject", { ns: "cli", path: packageJsonPath }));
|
|
1971
|
+
}
|
|
1972
|
+
return raw;
|
|
1973
|
+
} catch (error) {
|
|
1974
|
+
if (error instanceof Error) {
|
|
1975
|
+
throw error;
|
|
1976
|
+
}
|
|
1977
|
+
throw new Error(i18n.t("packageReadFailed", { ns: "cli", path: packageJsonPath }));
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
function readRequiredPackageString(packageJson, key) {
|
|
1981
|
+
const value = packageJson[key];
|
|
1982
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
1983
|
+
throw new Error(i18n.t("packageStringRequired", { ns: "cli", key }));
|
|
1984
|
+
}
|
|
1985
|
+
return value.trim();
|
|
1986
|
+
}
|
|
1987
|
+
async function writeWorkflowPackageId(packageJsonPath, workflowId) {
|
|
1988
|
+
const packageJson = await readPackageJsonFile(packageJsonPath);
|
|
1989
|
+
await writeFile2(
|
|
1990
|
+
packageJsonPath,
|
|
1991
|
+
`${JSON.stringify({
|
|
1992
|
+
...packageJson,
|
|
1993
|
+
id: workflowId
|
|
1994
|
+
}, null, 2)}
|
|
1995
|
+
`
|
|
1996
|
+
);
|
|
1997
|
+
}
|
|
1998
|
+
async function uploadPackage(options) {
|
|
1999
|
+
const archiveBuffer = await readFile3(options.archivePath);
|
|
2000
|
+
const archiveName = path4.basename(options.archivePath);
|
|
2001
|
+
const response = await fetch(
|
|
2002
|
+
`${trimTrailingSlash(options.server)}/api/workflows/${encodeURIComponent(options.workflowId)}/package?fileName=${encodeURIComponent(archiveName)}`,
|
|
2003
|
+
{
|
|
2004
|
+
method: "POST",
|
|
2005
|
+
headers: {
|
|
2006
|
+
authorization: `Bearer ${options.token}`,
|
|
2007
|
+
"content-type": "application/octet-stream",
|
|
2008
|
+
"x-workflow-package-name": archiveName,
|
|
2009
|
+
"x-workflow-locale": getCurrentLocale()
|
|
2010
|
+
},
|
|
2011
|
+
body: new Blob([archiveBuffer])
|
|
2012
|
+
}
|
|
2013
|
+
);
|
|
2014
|
+
return response.json();
|
|
2015
|
+
}
|
|
2016
|
+
async function waitForPreparation(options) {
|
|
2017
|
+
let previousStage = "";
|
|
2018
|
+
while (true) {
|
|
2019
|
+
const response = await fetchApi({
|
|
2020
|
+
server: options.server,
|
|
2021
|
+
token: options.token,
|
|
2022
|
+
path: `/api/workflows/${encodeURIComponent(options.workflowId)}/preparations/${encodeURIComponent(options.jobId)}`
|
|
2023
|
+
});
|
|
2024
|
+
if (response.errCode !== 0 || response.data === void 0) {
|
|
2025
|
+
return response;
|
|
2026
|
+
}
|
|
2027
|
+
if (response.data.stage !== previousStage) {
|
|
2028
|
+
previousStage = response.data.stage;
|
|
2029
|
+
process.stderr.write(`${i18n.t("preparationProgress", {
|
|
2030
|
+
ns: "cli",
|
|
2031
|
+
status: response.data.status,
|
|
2032
|
+
stage: response.data.stage
|
|
2033
|
+
})}
|
|
2034
|
+
`);
|
|
2035
|
+
}
|
|
2036
|
+
if (response.data.status === "success") {
|
|
2037
|
+
return {
|
|
2038
|
+
errCode: 0,
|
|
2039
|
+
errMessage: "",
|
|
2040
|
+
data: response.data.result
|
|
2041
|
+
};
|
|
2042
|
+
}
|
|
2043
|
+
if (response.data.status === "failed") {
|
|
2044
|
+
return {
|
|
2045
|
+
errCode: 500,
|
|
2046
|
+
errMessage: response.data.failureReason ?? `Preparation failed during ${response.data.stage}.`,
|
|
2047
|
+
data: response.data
|
|
2048
|
+
};
|
|
2049
|
+
}
|
|
2050
|
+
await delay(options.pollIntervalMs ?? 1e3);
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
async function getPreparation(options) {
|
|
2054
|
+
return fetchApi({
|
|
2055
|
+
server: options.server,
|
|
2056
|
+
token: options.token,
|
|
2057
|
+
path: `/api/workflows/${encodeURIComponent(options.workflowId)}/preparations/${encodeURIComponent(options.jobId)}`
|
|
2058
|
+
});
|
|
2059
|
+
}
|
|
2060
|
+
async function fetchApi(options) {
|
|
2061
|
+
const headers = {};
|
|
2062
|
+
if (options.auth !== false) {
|
|
2063
|
+
headers.authorization = `Bearer ${options.token}`;
|
|
2064
|
+
}
|
|
2065
|
+
if (options.body !== void 0) {
|
|
2066
|
+
headers["content-type"] = "application/json";
|
|
2067
|
+
}
|
|
2068
|
+
headers["x-workflow-locale"] = getCurrentLocale();
|
|
2069
|
+
const response = await fetch(`${trimTrailingSlash(options.server)}${options.path}`, {
|
|
2070
|
+
method: options.method ?? "GET",
|
|
2071
|
+
headers,
|
|
2072
|
+
body: options.body === void 0 ? void 0 : JSON.stringify(options.body)
|
|
2073
|
+
});
|
|
2074
|
+
return response.json();
|
|
2075
|
+
}
|
|
2076
|
+
async function copyWorkflowSource(sourceDir, targetDir, options = {}) {
|
|
2077
|
+
if (options.projectGroupSafe !== true) return copyWorkflowUploadFiles(sourceDir, targetDir);
|
|
2078
|
+
const plan = await createWorkflowUploadPlan(sourceDir);
|
|
2079
|
+
const files = plan.files.filter((file) => !isProjectGroupHostDataPath(file.path));
|
|
2080
|
+
const forbiddenRequiredPath = plan.requiredPaths.find((requiredPath) => isProjectGroupHostDataPath(requiredPath));
|
|
2081
|
+
if (forbiddenRequiredPath !== void 0) {
|
|
2082
|
+
throw new Error(i18n.t("projectGroupRequiredPathForbidden", {
|
|
2083
|
+
ns: "cli",
|
|
2084
|
+
path: JSON.stringify(forbiddenRequiredPath)
|
|
2085
|
+
}));
|
|
2086
|
+
}
|
|
2087
|
+
await mkdir3(targetDir, { recursive: true });
|
|
2088
|
+
for (const file of files) {
|
|
2089
|
+
const targetPath = path4.join(targetDir, ...file.path.split("/"));
|
|
2090
|
+
await mkdir3(path4.dirname(targetPath), { recursive: true });
|
|
2091
|
+
await copyFile2(file.absolutePath, targetPath);
|
|
2092
|
+
}
|
|
2093
|
+
return {
|
|
2094
|
+
...plan,
|
|
2095
|
+
files,
|
|
2096
|
+
ignoredPaths: [...plan.ignoredPaths, ...plan.files.filter((file) => isProjectGroupHostDataPath(file.path)).map((file) => file.path)].sort((left, right) => left.localeCompare(right)),
|
|
2097
|
+
totalBytes: files.reduce((total, file) => total + file.size, 0)
|
|
2098
|
+
};
|
|
2099
|
+
}
|
|
2100
|
+
async function stageWorkflowWithLocalDependencies(options) {
|
|
2101
|
+
const queue = [options.workflowName];
|
|
2102
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2103
|
+
while (queue.length > 0) {
|
|
2104
|
+
const currentWorkflowName = queue.shift();
|
|
2105
|
+
if (!currentWorkflowName || visited.has(currentWorkflowName)) {
|
|
2106
|
+
continue;
|
|
2107
|
+
}
|
|
2108
|
+
visited.add(currentWorkflowName);
|
|
2109
|
+
const sourceDir = currentWorkflowName === options.workflowName ? options.workflowDir : path4.join(options.workspaceRoot, currentWorkflowName);
|
|
2110
|
+
if (!existsSync2(path4.join(sourceDir, "package.json"))) {
|
|
2111
|
+
throw new Error(i18n.t("localDependencyMissingPackage", {
|
|
2112
|
+
ns: "cli",
|
|
2113
|
+
workflow: options.workflowName,
|
|
2114
|
+
dependency: currentWorkflowName,
|
|
2115
|
+
path: sourceDir
|
|
2116
|
+
}));
|
|
2117
|
+
}
|
|
2118
|
+
const uploadPlan = await copyWorkflowSource(
|
|
2119
|
+
sourceDir,
|
|
2120
|
+
path4.join(options.stagingRoot, currentWorkflowName),
|
|
2121
|
+
{ projectGroupSafe: options.projectGroupSafe }
|
|
2122
|
+
);
|
|
2123
|
+
if (currentWorkflowName === options.workflowName && !uploadPlan.workflowIgnoreConfigured) {
|
|
2124
|
+
process.stderr.write(`${i18n.t("workflowIgnoreWarning", {
|
|
2125
|
+
ns: "cli",
|
|
2126
|
+
files: uploadPlan.files.length,
|
|
2127
|
+
bytes: uploadPlan.totalBytes
|
|
2128
|
+
})}
|
|
2129
|
+
`);
|
|
2130
|
+
}
|
|
2131
|
+
for (const dependency of await findLocalWorkflowDependencies(sourceDir)) {
|
|
2132
|
+
if (!visited.has(dependency)) {
|
|
2133
|
+
queue.push(dependency);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
async function findLocalWorkflowDependencies(sourceDir) {
|
|
2139
|
+
const discovered = /* @__PURE__ */ new Set();
|
|
2140
|
+
for (const fileName of ["index.ts", "interface.ts", "globals.d.ts"]) {
|
|
2141
|
+
const filePath = path4.join(sourceDir, fileName);
|
|
2142
|
+
if (!existsSync2(filePath)) {
|
|
2143
|
+
continue;
|
|
2144
|
+
}
|
|
2145
|
+
const source = await readFile3(filePath, "utf8");
|
|
2146
|
+
for (const match of source.matchAll(/(?:from\s+|reference path=)\"(\.\.\/([^/\"']+)(?:\/[^\"']*)?)\"/g)) {
|
|
2147
|
+
const workflowName = match[2]?.trim();
|
|
2148
|
+
if (workflowName) {
|
|
2149
|
+
discovered.add(workflowName);
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
for (const match of source.matchAll(/(?:from\s+|reference path=)'(\.\.\/([^/'"]+)(?:\/[^'"]*)?)'/g)) {
|
|
2153
|
+
const workflowName = match[2]?.trim();
|
|
2154
|
+
if (workflowName) {
|
|
2155
|
+
discovered.add(workflowName);
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
return [...discovered].sort((left, right) => left.localeCompare(right));
|
|
2160
|
+
}
|
|
2161
|
+
function normalizeWorkflowFilePath(filePath) {
|
|
2162
|
+
const normalized = filePath.replaceAll("\\", "/").replace(/^\.\/+/, "");
|
|
2163
|
+
if (normalized === "" || normalized.startsWith("/") || /^[a-zA-Z]:/.test(normalized) || normalized.split("/").some((part) => part === "" || part === "." || part === "..")) {
|
|
2164
|
+
throw new Error(i18n.t("invalidWorkflowFilePath", { ns: "cli", path: filePath }));
|
|
2165
|
+
}
|
|
2166
|
+
return normalized;
|
|
2167
|
+
}
|
|
2168
|
+
function decodeWorkflowFileContent(file) {
|
|
2169
|
+
if (file.encoding === void 0) {
|
|
2170
|
+
return file.content;
|
|
2171
|
+
}
|
|
2172
|
+
if (file.encoding !== "base64") {
|
|
2173
|
+
throw new Error(i18n.t("unsupportedWorkflowEncoding", { ns: "cli", path: file.path }));
|
|
2174
|
+
}
|
|
2175
|
+
const normalized = file.content.replace(/=+$/, "");
|
|
2176
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(file.content)) {
|
|
2177
|
+
throw new Error(i18n.t("invalidBase64Content", { ns: "cli", path: file.path }));
|
|
2178
|
+
}
|
|
2179
|
+
const decoded = Buffer.from(file.content, "base64");
|
|
2180
|
+
if (decoded.toString("base64").replace(/=+$/, "") !== normalized) {
|
|
2181
|
+
throw new Error(i18n.t("invalidBase64Content", { ns: "cli", path: file.path }));
|
|
2182
|
+
}
|
|
2183
|
+
return decoded;
|
|
2184
|
+
}
|
|
2185
|
+
function hashWorkflowSourceFiles(files) {
|
|
2186
|
+
const hash = createHash("sha256");
|
|
2187
|
+
const normalizedFiles = files.map((file) => ({
|
|
2188
|
+
file,
|
|
2189
|
+
path: normalizeWorkflowFilePath(file.path)
|
|
2190
|
+
})).sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
2191
|
+
for (const item of normalizedFiles) {
|
|
2192
|
+
hash.update(item.path);
|
|
2193
|
+
hash.update("\0");
|
|
2194
|
+
hash.update(decodeWorkflowFileContent(item.file));
|
|
2195
|
+
hash.update("\0");
|
|
2196
|
+
}
|
|
2197
|
+
return hash.digest("hex");
|
|
2198
|
+
}
|
|
2199
|
+
function parseArgs(argv) {
|
|
2200
|
+
const [command = "", ...rest] = argv;
|
|
2201
|
+
const positional = [];
|
|
2202
|
+
const authConfig = readCliAuthConfigSync();
|
|
2203
|
+
const options = {
|
|
2204
|
+
server: process.env.WORKFLOW_SERVER_URL ?? authConfig.serverUrl ?? DEFAULT_SERVER_URL,
|
|
2205
|
+
token: process.env.WORKFLOW_SERVER_ADMIN_KEY ?? authConfig.apiKey ?? "",
|
|
2206
|
+
dependencies: [],
|
|
2207
|
+
dataGrants: []
|
|
2208
|
+
};
|
|
2209
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
2210
|
+
const arg = rest[index];
|
|
2211
|
+
if (arg === "--") {
|
|
2212
|
+
positional.push(...rest.slice(index + 1));
|
|
2213
|
+
break;
|
|
2214
|
+
}
|
|
2215
|
+
switch (arg) {
|
|
2216
|
+
case "--server":
|
|
2217
|
+
options.server = requireValue(rest, index += 1, "--server");
|
|
2218
|
+
break;
|
|
2219
|
+
case "--token":
|
|
2220
|
+
options.token = requireValue(rest, index += 1, "--token");
|
|
2221
|
+
break;
|
|
2222
|
+
case "--target":
|
|
2223
|
+
options.target = requireValue(rest, index += 1, "--target");
|
|
2224
|
+
break;
|
|
2225
|
+
case "--entrypoint":
|
|
2226
|
+
options.entrypoint = requireValue(rest, index += 1, "--entrypoint");
|
|
2227
|
+
break;
|
|
2228
|
+
case "--version":
|
|
2229
|
+
throw new Error(i18n.t("versionOptionUnsupported", { ns: "cli" }));
|
|
2230
|
+
case "--release-log":
|
|
2231
|
+
options.releaseLog = requireValue(rest, index += 1, "--release-log");
|
|
2232
|
+
break;
|
|
2233
|
+
case "--file":
|
|
2234
|
+
options.file = requireValue(rest, index += 1, "--file");
|
|
2235
|
+
break;
|
|
2236
|
+
case "--output":
|
|
2237
|
+
options.output = requireValue(rest, index += 1, "--output");
|
|
2238
|
+
break;
|
|
2239
|
+
case "--path":
|
|
2240
|
+
options.path = requireValue(rest, index += 1, "--path");
|
|
2241
|
+
break;
|
|
2242
|
+
case "--create":
|
|
2243
|
+
options.create = true;
|
|
2244
|
+
break;
|
|
2245
|
+
case "--no-wait":
|
|
2246
|
+
options.noWait = true;
|
|
2247
|
+
break;
|
|
2248
|
+
case "--dependency":
|
|
2249
|
+
options.dependencies.push(requireValue(rest, index += 1, "--dependency"));
|
|
2250
|
+
break;
|
|
2251
|
+
case "--grant-data-access":
|
|
2252
|
+
options.dataGrants.push(requireValue(rest, index += 1, "--grant-data-access"));
|
|
2253
|
+
break;
|
|
2254
|
+
case "--project-group":
|
|
2255
|
+
options.projectGroup = true;
|
|
2256
|
+
break;
|
|
2257
|
+
case "--publish-group":
|
|
2258
|
+
options.publishGroup = true;
|
|
2259
|
+
break;
|
|
2260
|
+
case "--deployment":
|
|
2261
|
+
options.deployment = requireValue(rest, index += 1, "--deployment");
|
|
2262
|
+
break;
|
|
2263
|
+
case "--source-mode": {
|
|
2264
|
+
const value = requireValue(rest, index += 1, "--source-mode");
|
|
2265
|
+
if (value !== "bundled" && value !== "source") {
|
|
2266
|
+
throw new Error(i18n.t("invalidSourceMode", { ns: "cli" }));
|
|
2267
|
+
}
|
|
2268
|
+
options.sourceMode = value;
|
|
2269
|
+
break;
|
|
2270
|
+
}
|
|
2271
|
+
default:
|
|
2272
|
+
positional.push(arg);
|
|
2273
|
+
break;
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
if (command === "upload" && options.noWait === true && options.releaseLog !== void 0) {
|
|
2277
|
+
throw new Error(i18n.t("noWaitReleaseLogConflict", { ns: "cli" }));
|
|
2278
|
+
}
|
|
2279
|
+
if (options.noWait === true && command !== "upload") {
|
|
2280
|
+
throw new Error(i18n.t("noWaitCommandUnsupported", { ns: "cli" }));
|
|
2281
|
+
}
|
|
2282
|
+
if (command === "upload" && options.noWait === true && shouldUseProjectGroupUpload(options)) {
|
|
2283
|
+
throw new Error(i18n.t("noWaitProjectGroupUnsupported", { ns: "cli" }));
|
|
2284
|
+
}
|
|
2285
|
+
if (options.dependencies.length > 0 && command !== "pack" && command !== "upload" && command !== "publish") {
|
|
2286
|
+
throw new Error(i18n.t("dependencyOptionCommandUnsupported", { ns: "cli" }));
|
|
2287
|
+
}
|
|
2288
|
+
if (options.dataGrants.length > 0 && command !== "upload" && command !== "publish") {
|
|
2289
|
+
throw new Error(i18n.t("grantDataAccessCommandUnsupported", { ns: "cli" }));
|
|
2290
|
+
}
|
|
2291
|
+
if (options.deployment !== void 0 && command !== "publish") {
|
|
2292
|
+
throw new Error(i18n.t("deploymentCommandUnsupported", { ns: "cli" }));
|
|
2293
|
+
}
|
|
2294
|
+
if (options.publishGroup === true && command !== "upload") {
|
|
2295
|
+
throw new Error(i18n.t("publishGroupCommandUnsupported", { ns: "cli" }));
|
|
2296
|
+
}
|
|
2297
|
+
if (options.projectGroup === true && command !== "pack" && command !== "upload" && command !== "publish") {
|
|
2298
|
+
throw new Error(i18n.t("projectGroupCommandUnsupported", { ns: "cli" }));
|
|
2299
|
+
}
|
|
2300
|
+
if (options.deployment !== void 0 && (options.projectGroup === true || options.dependencies.length > 0 || options.dataGrants.length > 0 || options.path !== void 0 || options.file !== void 0 || options.output !== void 0 || options.create === true || options.releaseLog !== void 0 || options.sourceMode !== void 0)) {
|
|
2301
|
+
throw new Error(i18n.t("deploymentOptionsConflict", { ns: "cli" }));
|
|
2302
|
+
}
|
|
2303
|
+
return {
|
|
2304
|
+
command,
|
|
2305
|
+
positional,
|
|
2306
|
+
options
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
function requireLogin(parsed) {
|
|
2310
|
+
if (parsed.options.server.trim() === "" || parsed.options.token.trim() === "") {
|
|
2311
|
+
throw new Error(i18n.t("notLoggedIn", { ns: "cli" }));
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
function requirePositional(parsed, index, name) {
|
|
2315
|
+
const value = parsed.positional[index];
|
|
2316
|
+
if (value === void 0 || value.trim() === "") {
|
|
2317
|
+
throw new Error(i18n.t("missingPositional", { ns: "cli", name }));
|
|
2318
|
+
}
|
|
2319
|
+
return value;
|
|
2320
|
+
}
|
|
2321
|
+
function requireValue(argv, index, name) {
|
|
2322
|
+
const value = argv[index];
|
|
2323
|
+
if (value === void 0 || value.trim() === "") {
|
|
2324
|
+
throw new Error(i18n.t("missingOptionValue", { ns: "cli", name }));
|
|
2325
|
+
}
|
|
2326
|
+
return value;
|
|
2327
|
+
}
|
|
2328
|
+
function runCommand(command, args, options = {}) {
|
|
2329
|
+
return new Promise((resolve) => {
|
|
2330
|
+
const child = spawn(command, args, {
|
|
2331
|
+
cwd: options.cwd,
|
|
2332
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
2333
|
+
});
|
|
2334
|
+
const stdout = [];
|
|
2335
|
+
const stderr = [];
|
|
2336
|
+
child.stdout.on("data", (chunk) => stdout.push(chunk));
|
|
2337
|
+
child.stderr.on("data", (chunk) => stderr.push(chunk));
|
|
2338
|
+
child.on("error", (error) => {
|
|
2339
|
+
resolve({
|
|
2340
|
+
exitCode: 1,
|
|
2341
|
+
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
2342
|
+
stderr: `${Buffer.concat(stderr).toString("utf8")}${error.message}`
|
|
2343
|
+
});
|
|
2344
|
+
});
|
|
2345
|
+
child.on("close", (exitCode) => {
|
|
2346
|
+
resolve({
|
|
2347
|
+
exitCode: exitCode ?? 1,
|
|
2348
|
+
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
2349
|
+
stderr: Buffer.concat(stderr).toString("utf8")
|
|
2350
|
+
});
|
|
2351
|
+
});
|
|
2352
|
+
});
|
|
2353
|
+
}
|
|
2354
|
+
function printApiResponse(response) {
|
|
2355
|
+
console.log(JSON.stringify(response, null, 2));
|
|
2356
|
+
}
|
|
2357
|
+
function assertApiOk(response) {
|
|
2358
|
+
if (response.errCode !== 0) {
|
|
2359
|
+
if (response.errCode === 403) {
|
|
2360
|
+
process.stderr.write(`${formatForbiddenMessage(response)}
|
|
2361
|
+
`);
|
|
2362
|
+
}
|
|
2363
|
+
process.exitCode = 1;
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
function formatForbiddenMessage(response) {
|
|
2367
|
+
const message = response.errMessage.toLowerCase();
|
|
2368
|
+
if (message.includes("blocked from running")) {
|
|
2369
|
+
return i18n.t("forbiddenBlocked", { ns: "cli" });
|
|
2370
|
+
}
|
|
2371
|
+
if (message.includes("permission denied")) {
|
|
2372
|
+
return i18n.t("forbiddenNoPermission", { ns: "cli" });
|
|
2373
|
+
}
|
|
2374
|
+
return i18n.t("forbiddenFallback", { ns: "cli", message: response.errMessage });
|
|
2375
|
+
}
|
|
2376
|
+
function printUsage() {
|
|
2377
|
+
console.log(i18n.t("workspaceHelp", { ns: "cli" }));
|
|
2378
|
+
}
|
|
2379
|
+
function resolveRepoRoot(startDir = process.cwd()) {
|
|
2380
|
+
if (process.env.WORKFLOW_REPO_ROOT !== void 0) {
|
|
2381
|
+
return path4.resolve(process.env.WORKFLOW_REPO_ROOT);
|
|
2382
|
+
}
|
|
2383
|
+
for (const candidate of [startDir, CLI_MODULE_DIR]) {
|
|
2384
|
+
const repoRoot = findRepoRoot2(candidate);
|
|
2385
|
+
if (repoRoot !== void 0) {
|
|
2386
|
+
return repoRoot;
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
return process.cwd();
|
|
2390
|
+
}
|
|
2391
|
+
function findRepoRoot2(startDir) {
|
|
2392
|
+
let current = path4.resolve(startDir);
|
|
2393
|
+
while (true) {
|
|
2394
|
+
if (isRepoRoot(current)) {
|
|
2395
|
+
return current;
|
|
2396
|
+
}
|
|
2397
|
+
const parent = path4.dirname(current);
|
|
2398
|
+
if (parent === current) {
|
|
2399
|
+
return void 0;
|
|
2400
|
+
}
|
|
2401
|
+
current = parent;
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
function isRepoRoot(candidate) {
|
|
2405
|
+
return existsSync2(path4.join(candidate, "pnpm-workspace.yaml")) && existsSync2(path4.join(candidate, "package.json")) && existsSync2(path4.join(candidate, "workspace", "package.json")) && existsSync2(path4.join(candidate, "packages", "cli", "package.json"));
|
|
2406
|
+
}
|
|
2407
|
+
function trimTrailingSlash(value) {
|
|
2408
|
+
return value.replace(/\/+$/, "");
|
|
2409
|
+
}
|
|
2410
|
+
function createTimestamp() {
|
|
2411
|
+
const date = /* @__PURE__ */ new Date();
|
|
2412
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
2413
|
+
return [
|
|
2414
|
+
date.getUTCFullYear(),
|
|
2415
|
+
pad(date.getUTCMonth() + 1),
|
|
2416
|
+
pad(date.getUTCDate()),
|
|
2417
|
+
"-",
|
|
2418
|
+
pad(date.getUTCHours()),
|
|
2419
|
+
pad(date.getUTCMinutes()),
|
|
2420
|
+
pad(date.getUTCSeconds())
|
|
2421
|
+
].join("");
|
|
2422
|
+
}
|
|
2423
|
+
async function waitForDeviceToken(options) {
|
|
2424
|
+
const expiresAtMs = new Date(options.expiresAt).getTime();
|
|
2425
|
+
const pollMs = Math.max(1e3, options.intervalSeconds * 1e3);
|
|
2426
|
+
while (!Number.isFinite(expiresAtMs) || Date.now() < expiresAtMs) {
|
|
2427
|
+
const response = await fetchApi({
|
|
2428
|
+
server: options.server,
|
|
2429
|
+
token: "",
|
|
2430
|
+
path: "/api/auth/device/token",
|
|
2431
|
+
method: "POST",
|
|
2432
|
+
body: { deviceCode: options.deviceCode },
|
|
2433
|
+
auth: false
|
|
2434
|
+
});
|
|
2435
|
+
if (response.errCode === 0) {
|
|
2436
|
+
return response;
|
|
2437
|
+
}
|
|
2438
|
+
if (response.errMessage === "slow_down") {
|
|
2439
|
+
const retryAfterSeconds = readRetryAfterSeconds(response.data) ?? Math.max(options.intervalSeconds * 2, 5);
|
|
2440
|
+
await delay(retryAfterSeconds * 1e3);
|
|
2441
|
+
continue;
|
|
2442
|
+
}
|
|
2443
|
+
if (response.errMessage !== "authorization_pending") {
|
|
2444
|
+
return response;
|
|
2445
|
+
}
|
|
2446
|
+
await delay(pollMs);
|
|
2447
|
+
}
|
|
2448
|
+
return {
|
|
2449
|
+
errCode: 409,
|
|
2450
|
+
errMessage: "expired_token"
|
|
2451
|
+
};
|
|
2452
|
+
}
|
|
2453
|
+
async function openExternalBrowser(url) {
|
|
2454
|
+
const commands = process.platform === "darwin" ? [["open", url]] : process.platform === "win32" ? [["cmd", "/c", "start", "", url]] : [["xdg-open", url]];
|
|
2455
|
+
for (const [command, ...args] of commands) {
|
|
2456
|
+
const result = await runCommand(command, args);
|
|
2457
|
+
if (result.exitCode === 0) {
|
|
2458
|
+
return;
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
function delay(ms) {
|
|
2463
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2464
|
+
}
|
|
2465
|
+
function readRetryAfterSeconds(data) {
|
|
2466
|
+
if (typeof data !== "object" || data === null) {
|
|
2467
|
+
return void 0;
|
|
2468
|
+
}
|
|
2469
|
+
const value = data.retryAfterSeconds;
|
|
2470
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
2471
|
+
}
|
|
2472
|
+
if (process.argv[1] !== void 0 && import.meta.url === pathToFileURL(path4.resolve(process.argv[1])).href) {
|
|
2473
|
+
main().catch((error) => {
|
|
2474
|
+
console.error(translateWorkflowCodeError(error));
|
|
2475
|
+
process.exitCode = 1;
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2478
|
+
export {
|
|
2479
|
+
decodeWorkflowFileContent,
|
|
2480
|
+
ensureWorkflowProjectCreated,
|
|
2481
|
+
findLocalWorkflowDependencies,
|
|
2482
|
+
formatForbiddenMessage,
|
|
2483
|
+
getPreparation,
|
|
2484
|
+
main,
|
|
2485
|
+
materializeProjectGroupDownload,
|
|
2486
|
+
normalizeWorkflowFilePath,
|
|
2487
|
+
packProjectGroup,
|
|
2488
|
+
packWorkflow,
|
|
2489
|
+
parseArgs,
|
|
2490
|
+
readPackageJsonFile,
|
|
2491
|
+
readProjectGroupContexts,
|
|
2492
|
+
readWorkflowPackageContext,
|
|
2493
|
+
resolveExplicitDataGrants,
|
|
2494
|
+
resolveRemoteWorkflowId,
|
|
2495
|
+
resolveRepoRoot,
|
|
2496
|
+
shouldUseProjectGroupUpload,
|
|
2497
|
+
stageWorkflowWithLocalDependencies,
|
|
2498
|
+
waitForDeviceToken,
|
|
2499
|
+
waitForPreparation,
|
|
2500
|
+
writeWorkflowPackageId
|
|
2501
|
+
};
|