@workflow-code/cli 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __commonJS = (cb, mod) => function __require() {
9
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+
28
+ export {
29
+ __commonJS,
30
+ __toESM
31
+ };
package/dist/index.js CHANGED
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
+ import "./chunk-FYS2JH42.js";
2
3
 
3
4
  // src/index.ts
4
5
  import path from "path";
5
6
  import { pathToFileURL } from "url";
6
7
  async function main(argv = process.argv.slice(2)) {
7
8
  if (argv[0] === "workspace") {
8
- const workspaceCli = await import("./workspace-WJOCEGA6.js");
9
+ const workspaceCli = await import("./workspace-QUP6RU65.js");
9
10
  await workspaceCli.main(argv.slice(1));
10
11
  return typeof process.exitCode === "number" ? process.exitCode : 0;
11
12
  }
@@ -16,7 +17,7 @@ async function main(argv = process.argv.slice(2)) {
16
17
  }
17
18
  const { register } = await import("tsx/esm/api");
18
19
  register();
19
- const localCli = await import("./local-XBULJ25O.js");
20
+ const localCli = await import("./local-6VUUVT3C.js");
20
21
  return localCli.main(argv);
21
22
  }
22
23
  function formatHelp() {
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import "./chunk-FYS2JH42.js";
2
3
 
3
4
  // src/local.ts
4
5
  import path from "path";
@@ -1,11 +1,474 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ __commonJS,
4
+ __toESM
5
+ } from "./chunk-FYS2JH42.js";
6
+
7
+ // ../../node_modules/.pnpm/ignore@7.0.6/node_modules/ignore/index.js
8
+ var require_ignore = __commonJS({
9
+ "../../node_modules/.pnpm/ignore@7.0.6/node_modules/ignore/index.js"(exports, module) {
10
+ "use strict";
11
+ function makeArray(subject) {
12
+ return Array.isArray(subject) ? subject : [subject];
13
+ }
14
+ var UNDEFINED = void 0;
15
+ var EMPTY = "";
16
+ var SPACE = " ";
17
+ var ESCAPE = "\\";
18
+ var REGEX_TEST_BLANK_LINE = /^\s+$/;
19
+ var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
20
+ var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
21
+ var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
22
+ var REGEX_SPLITALL_CRLF = /\r?\n/g;
23
+ var REGEX_TEST_INVALID_PATH = /^\.{0,2}\/|^\.{1,2}$/;
24
+ var REGEX_TEST_TRAILING_SLASH = /\/$/;
25
+ var SLASH = "/";
26
+ var TMP_KEY_IGNORE = "node-ignore";
27
+ if (typeof Symbol !== "undefined") {
28
+ TMP_KEY_IGNORE = /* @__PURE__ */ Symbol.for("node-ignore");
29
+ }
30
+ var KEY_IGNORE = TMP_KEY_IGNORE;
31
+ var define = (object, key, value) => {
32
+ Object.defineProperty(object, key, { value });
33
+ return value;
34
+ };
35
+ var REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
36
+ var RETURN_FALSE = () => false;
37
+ var sanitizeRange = (range) => range.replace(
38
+ REGEX_REGEXP_RANGE,
39
+ (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) ? match : EMPTY
40
+ );
41
+ var negateRange = (range) => range.startsWith("!") || range.startsWith("\\^") ? `^${range.slice(range[0] === "!" ? 1 : 2)}` : range;
42
+ var cleanRangeBackSlash = (slashes) => {
43
+ const { length } = slashes;
44
+ return slashes.slice(0, length - length % 2);
45
+ };
46
+ var REPLACERS = [
47
+ [
48
+ // Remove BOM
49
+ // TODO:
50
+ // Other similar zero-width characters?
51
+ /^\uFEFF/,
52
+ () => EMPTY
53
+ ],
54
+ // > Trailing spaces are ignored unless they are quoted with backslash ("\")
55
+ [
56
+ // (a\ ) -> (a )
57
+ // (a ) -> (a)
58
+ // (a ) -> (a)
59
+ // (a \ ) -> (a )
60
+ /((?:\\\\)*?)(\\?\s+)$/,
61
+ (_, m1, m2) => m1 + (m2.indexOf("\\") === 0 ? SPACE : EMPTY)
62
+ ],
63
+ // Replace (\ ) with ' '
64
+ // (\ ) -> ' '
65
+ // (\\ ) -> '\\ '
66
+ // (\\\ ) -> '\\ '
67
+ [
68
+ /(\\+?)\s/g,
69
+ (_, m1) => {
70
+ const { length } = m1;
71
+ return m1.slice(0, length - length % 2) + SPACE;
72
+ }
73
+ ],
74
+ // Escape metacharacters
75
+ // which is written down by users but means special for regular expressions.
76
+ // > There are 12 characters with special meanings:
77
+ // > - the backslash \,
78
+ // > - the caret ^,
79
+ // > - the dollar sign $,
80
+ // > - the period or dot .,
81
+ // > - the vertical bar or pipe symbol |,
82
+ // > - the question mark ?,
83
+ // > - the asterisk or star *,
84
+ // > - the plus sign +,
85
+ // > - the opening parenthesis (,
86
+ // > - the closing parenthesis ),
87
+ // > - and the opening square bracket [,
88
+ // > - the opening curly brace {,
89
+ // > These special characters are often called "metacharacters".
90
+ [
91
+ /[\\$.|*+(){^]/g,
92
+ (match) => `\\${match}`
93
+ ],
94
+ [
95
+ // > a question mark (?) matches a single character
96
+ /(?!\\)\?/g,
97
+ () => "[^/]"
98
+ ],
99
+ // leading slash
100
+ [
101
+ // > A leading slash matches the beginning of the pathname.
102
+ // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
103
+ // A leading slash matches the beginning of the pathname
104
+ /^\//,
105
+ () => "^"
106
+ ],
107
+ // replace special metacharacter slash after the leading slash
108
+ [
109
+ /\//g,
110
+ () => "\\/"
111
+ ],
112
+ [
113
+ // > A leading "**" followed by a slash means match in all directories.
114
+ // > For example, "**/foo" matches file or directory "foo" anywhere,
115
+ // > the same as pattern "foo".
116
+ // > "**/foo/bar" matches file or directory "bar" anywhere that is directly
117
+ // > under directory "foo".
118
+ // Notice that the '*'s have been replaced as '\\*'
119
+ /^\^*(?:\\\*\\\*\\\/)+/,
120
+ // '**/foo' <-> 'foo'
121
+ () => "^(?:.*\\/)?"
122
+ ],
123
+ // starting
124
+ [
125
+ // there will be no leading '/'
126
+ // (which has been replaced by section "leading slash")
127
+ // If starts with '**', adding a '^' to the regular expression also works
128
+ /^(?=[^^])/,
129
+ function startingReplacer() {
130
+ return !/\/(?!$)/.test(this) ? "(?:^|\\/)" : "^";
131
+ }
132
+ ],
133
+ // two globstars
134
+ [
135
+ // Use lookahead assertions so that we could match more than one `'/**'`
136
+ /\\\/\\\*\\\*(?=\\\/|$)/g,
137
+ // Zero, one or several directories
138
+ // should not use '*', or it will be replaced by the next replacer
139
+ // Check if it is not the last `'/**'`
140
+ (_, index, str) => index + 6 < str.length ? "(?:\\/[^\\/]+)*" : "\\/.+"
141
+ ],
142
+ // normal intermediate wildcards
143
+ [
144
+ // Never replace escaped '*'
145
+ // ignore rule '\*' will match the path '*'
146
+ // 'abc.*/' -> go
147
+ // 'abc.*' -> skip this rule,
148
+ // coz trailing single wildcard will be handed by [trailing wildcard]
149
+ /(^|[^\\]+)(\\\*)+(?=.+)/g,
150
+ // '*.js' matches '.js'
151
+ // '*.js' doesn't match 'abc'
152
+ (_, p1, p2) => {
153
+ const unescaped = p2.replace(/\\\*/g, "[^\\/]*");
154
+ return p1 + unescaped;
155
+ }
156
+ ],
157
+ [
158
+ // unescape, revert step 3 except for back slash
159
+ // For example, if a user escape a '\\*',
160
+ // after step 3, the result will be '\\\\\\*'
161
+ /\\\\\\(?=[$.|*+(){^])/g,
162
+ () => ESCAPE
163
+ ],
164
+ [
165
+ // '\\\\' -> '\\'
166
+ /\\\\/g,
167
+ () => ESCAPE
168
+ ],
169
+ [
170
+ // > The range notation, e.g. [a-zA-Z],
171
+ // > can be used to match one of the characters in a range.
172
+ // `\` is escaped by step 3
173
+ /(\\)?\[([^\]/]*?)(\\*)($|\])/g,
174
+ (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}` : close === "]" ? endEscape.length % 2 === 0 ? `[${negateRange(sanitizeRange(range))}${endEscape}]` : "[]" : "[]"
175
+ ],
176
+ // ending
177
+ [
178
+ // 'js' will not match 'js.'
179
+ // 'ab' will not match 'abc'
180
+ /(?:[^*])$/,
181
+ // WTF!
182
+ // https://git-scm.com/docs/gitignore
183
+ // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
184
+ // which re-fixes #24, #38
185
+ // > If there is a separator at the end of the pattern then the pattern
186
+ // > will only match directories, otherwise the pattern can match both
187
+ // > files and directories.
188
+ // 'js*' will not match 'a.js'
189
+ // 'js/' will not match 'a.js'
190
+ // 'js' will match 'a.js' and 'a.js/'
191
+ (match) => /\/$/.test(match) ? `${match}$` : `${match}(?=$|\\/$)`
192
+ ]
193
+ ];
194
+ var REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\\\*$/;
195
+ var MODE_IGNORE = "regex";
196
+ var MODE_CHECK_IGNORE = "checkRegex";
197
+ var UNDERSCORE = "_";
198
+ var TRAILING_WILD_CARD_REPLACERS = {
199
+ [MODE_IGNORE](_, p1) {
200
+ const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
201
+ return `${prefix}(?=$|\\/$)`;
202
+ },
203
+ [MODE_CHECK_IGNORE](_, p1) {
204
+ const prefix = p1 ? `${p1}[^/]*` : "[^/]*";
205
+ return `${prefix}(?=$|\\/$)`;
206
+ }
207
+ };
208
+ var makeRegexPrefix = (pattern) => REPLACERS.reduce(
209
+ (prev, [matcher, replacer]) => prev.replace(matcher, replacer.bind(pattern)),
210
+ pattern
211
+ );
212
+ var isString = (subject) => typeof subject === "string";
213
+ var checkPattern = (pattern) => pattern && isString(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0;
214
+ var splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF).filter(Boolean);
215
+ var IgnoreRule = class {
216
+ constructor(pattern, mark, body, ignoreCase, negative, prefix) {
217
+ this.pattern = pattern;
218
+ this.mark = mark;
219
+ this.negative = negative;
220
+ define(this, "body", body);
221
+ define(this, "ignoreCase", ignoreCase);
222
+ define(this, "regexPrefix", prefix);
223
+ }
224
+ get regex() {
225
+ const key = UNDERSCORE + MODE_IGNORE;
226
+ if (this[key]) {
227
+ return this[key];
228
+ }
229
+ return this._make(MODE_IGNORE, key);
230
+ }
231
+ get checkRegex() {
232
+ const key = UNDERSCORE + MODE_CHECK_IGNORE;
233
+ if (this[key]) {
234
+ return this[key];
235
+ }
236
+ return this._make(MODE_CHECK_IGNORE, key);
237
+ }
238
+ _make(mode, key) {
239
+ const str = this.regexPrefix.replace(
240
+ REGEX_REPLACE_TRAILING_WILDCARD,
241
+ // It does not need to bind pattern
242
+ TRAILING_WILD_CARD_REPLACERS[mode]
243
+ );
244
+ const regex = this.ignoreCase ? new RegExp(str, "i") : new RegExp(str);
245
+ return define(this, key, regex);
246
+ }
247
+ };
248
+ var createRule = ({
249
+ pattern,
250
+ mark
251
+ }, ignoreCase) => {
252
+ let negative = false;
253
+ let body = pattern;
254
+ if (body.indexOf("!") === 0) {
255
+ negative = true;
256
+ body = body.substr(1);
257
+ }
258
+ body = body.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, "!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, "#");
259
+ const regexPrefix = makeRegexPrefix(body);
260
+ return new IgnoreRule(
261
+ pattern,
262
+ mark,
263
+ body,
264
+ ignoreCase,
265
+ negative,
266
+ regexPrefix
267
+ );
268
+ };
269
+ var RuleManager = class {
270
+ constructor(ignoreCase) {
271
+ this._ignoreCase = ignoreCase;
272
+ this._rules = [];
273
+ }
274
+ _add(pattern) {
275
+ if (pattern && pattern[KEY_IGNORE]) {
276
+ this._rules = this._rules.concat(pattern._rules._rules);
277
+ this._added = true;
278
+ return;
279
+ }
280
+ if (isString(pattern)) {
281
+ pattern = {
282
+ pattern
283
+ };
284
+ }
285
+ if (checkPattern(pattern.pattern)) {
286
+ const rule = createRule(pattern, this._ignoreCase);
287
+ this._added = true;
288
+ this._rules.push(rule);
289
+ }
290
+ }
291
+ // @param {Array<string> | string | Ignore} pattern
292
+ add(pattern) {
293
+ this._added = false;
294
+ makeArray(
295
+ isString(pattern) ? splitPattern(pattern) : pattern
296
+ ).forEach(this._add, this);
297
+ return this._added;
298
+ }
299
+ // Test one single path without recursively checking parent directories
300
+ //
301
+ // - checkUnignored `boolean` whether should check if the path is unignored,
302
+ // setting `checkUnignored` to `false` could reduce additional
303
+ // path matching.
304
+ // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
305
+ // @returns {TestResult} true if a file is ignored
306
+ test(path5, checkUnignored, mode) {
307
+ let ignored = false;
308
+ let unignored = false;
309
+ let matchedRule;
310
+ this._rules.forEach((rule) => {
311
+ const { negative } = rule;
312
+ if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
313
+ return;
314
+ }
315
+ const matched = rule[mode].test(path5);
316
+ if (!matched) {
317
+ return;
318
+ }
319
+ ignored = !negative;
320
+ unignored = negative;
321
+ matchedRule = negative ? UNDEFINED : rule;
322
+ });
323
+ const ret = {
324
+ ignored,
325
+ unignored
326
+ };
327
+ if (matchedRule) {
328
+ ret.rule = matchedRule;
329
+ }
330
+ return ret;
331
+ }
332
+ };
333
+ var throwError = (message, Ctor) => {
334
+ throw new Ctor(message);
335
+ };
336
+ var checkPath = (path5, originalPath, doThrow) => {
337
+ if (!isString(path5)) {
338
+ return doThrow(
339
+ `path must be a string, but got \`${originalPath}\``,
340
+ TypeError
341
+ );
342
+ }
343
+ if (!path5) {
344
+ return doThrow(`path must not be empty`, TypeError);
345
+ }
346
+ if (checkPath.isNotRelative(path5)) {
347
+ const r = "`path.relative()`d";
348
+ return doThrow(
349
+ `path should be a ${r} string, but got "${originalPath}"`,
350
+ RangeError
351
+ );
352
+ }
353
+ return true;
354
+ };
355
+ var isNotRelative = (path5) => REGEX_TEST_INVALID_PATH.test(path5);
356
+ checkPath.isNotRelative = isNotRelative;
357
+ checkPath.convert = (p) => p;
358
+ var Ignore = class {
359
+ constructor({
360
+ ignorecase = true,
361
+ ignoreCase = ignorecase,
362
+ allowRelativePaths = false
363
+ } = {}) {
364
+ define(this, KEY_IGNORE, true);
365
+ this._rules = new RuleManager(ignoreCase);
366
+ this._strictPathCheck = !allowRelativePaths;
367
+ this._initCache();
368
+ }
369
+ _initCache() {
370
+ this._ignoreCache = /* @__PURE__ */ Object.create(null);
371
+ this._testCache = /* @__PURE__ */ Object.create(null);
372
+ }
373
+ add(pattern) {
374
+ if (this._rules.add(pattern)) {
375
+ this._initCache();
376
+ }
377
+ return this;
378
+ }
379
+ // legacy
380
+ addPattern(pattern) {
381
+ return this.add(pattern);
382
+ }
383
+ // @returns {TestResult}
384
+ _test(originalPath, cache, checkUnignored, slices) {
385
+ const path5 = originalPath && checkPath.convert(originalPath);
386
+ checkPath(
387
+ path5,
388
+ originalPath,
389
+ this._strictPathCheck ? throwError : RETURN_FALSE
390
+ );
391
+ return this._t(path5, cache, checkUnignored, slices);
392
+ }
393
+ checkIgnore(path5) {
394
+ if (!REGEX_TEST_TRAILING_SLASH.test(path5)) {
395
+ return this.test(path5);
396
+ }
397
+ const slices = path5.split(SLASH).filter(Boolean);
398
+ slices.pop();
399
+ if (slices.length) {
400
+ const parent = this._t(
401
+ slices.join(SLASH) + SLASH,
402
+ this._testCache,
403
+ true,
404
+ slices
405
+ );
406
+ if (parent.ignored) {
407
+ return parent;
408
+ }
409
+ }
410
+ return this._rules.test(path5, false, MODE_CHECK_IGNORE);
411
+ }
412
+ _t(path5, cache, checkUnignored, slices) {
413
+ if (path5 in cache) {
414
+ return cache[path5];
415
+ }
416
+ if (!slices) {
417
+ slices = path5.split(SLASH).filter(Boolean);
418
+ }
419
+ slices.pop();
420
+ if (!slices.length) {
421
+ return cache[path5] = this._rules.test(path5, checkUnignored, MODE_IGNORE);
422
+ }
423
+ const parent = this._t(
424
+ slices.join(SLASH) + SLASH,
425
+ cache,
426
+ checkUnignored,
427
+ slices
428
+ );
429
+ return cache[path5] = parent.ignored ? parent : this._rules.test(path5, checkUnignored, MODE_IGNORE);
430
+ }
431
+ ignores(path5) {
432
+ return this._test(path5, this._ignoreCache, false).ignored;
433
+ }
434
+ createFilter() {
435
+ return (path5) => !this.ignores(path5);
436
+ }
437
+ filter(paths) {
438
+ return makeArray(paths).filter(this.createFilter());
439
+ }
440
+ // @returns {TestResult}
441
+ test(path5) {
442
+ return this._test(path5, this._testCache, true);
443
+ }
444
+ };
445
+ var factory = (options) => new Ignore(options);
446
+ var isPathValid = (path5) => checkPath(path5 && checkPath.convert(path5), path5, RETURN_FALSE);
447
+ var setupWindows = () => {
448
+ const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
449
+ checkPath.convert = makePosix;
450
+ const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
451
+ checkPath.isNotRelative = (path5) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path5) || isNotRelative(path5);
452
+ };
453
+ if (
454
+ // Detect `process` so that it can run in browsers.
455
+ typeof process !== "undefined" && process.platform === "win32"
456
+ ) {
457
+ setupWindows();
458
+ }
459
+ module.exports = factory;
460
+ factory.default = factory;
461
+ module.exports.isPathValid = isPathValid;
462
+ define(module.exports, /* @__PURE__ */ Symbol.for("setupWindows"), setupWindows);
463
+ }
464
+ });
2
465
 
3
466
  // src/workspace.ts
4
467
  import { spawn } from "child_process";
5
468
  import { randomUUID } from "crypto";
6
469
  import { existsSync as existsSync2 } from "fs";
7
- import { cp, mkdir as mkdir2, readFile as readFile2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
8
- import path3 from "path";
470
+ import { mkdir as mkdir3, readFile as readFile3, rm as rm2, writeFile as writeFile2 } from "fs/promises";
471
+ import path4 from "path";
9
472
  import { fileURLToPath, pathToFileURL } from "url";
10
473
 
11
474
  // ../../shared/auth-config/index.ts
@@ -120,12 +583,267 @@ function isWorkflowUuid(value) {
120
583
  return WORKFLOW_UUID_PATTERN.test(value.trim());
121
584
  }
122
585
 
586
+ // ../../shared/project-upload/node.ts
587
+ import { copyFile, lstat, mkdir, readFile, readdir } from "fs/promises";
588
+ import path2 from "path";
589
+
590
+ // ../../shared/project-upload/index.ts
591
+ var import_ignore = __toESM(require_ignore(), 1);
592
+
593
+ // ../../shared/kanban-project/index.ts
594
+ var KANBAN_MANIFEST_FILE = "kanban.json";
595
+ var KANBAN_ENTRY_FILE = "index.html";
596
+ var KANBAN_ARTIFACT_DIR = ".";
597
+ function readPackageProjectType(value) {
598
+ if (!isRecord2(value)) return void 0;
599
+ const workflowCode = value["workflowCode"];
600
+ if (!isRecord2(workflowCode)) return void 0;
601
+ return workflowCode["projectType"] === "kanban" ? "kanban" : void 0;
602
+ }
603
+ function readKanbanArtifactConfig(value) {
604
+ if (!isRecord2(value)) {
605
+ throw new Error("package.json root must be an object.");
606
+ }
607
+ const workflowCode = value["workflowCode"];
608
+ if (!isRecord2(workflowCode)) {
609
+ throw new Error("package.json workflowCode must be an object for a Kanban project.");
610
+ }
611
+ const rawConfig = workflowCode["kanban"];
612
+ if (rawConfig !== void 0 && !isRecord2(rawConfig)) {
613
+ throw new Error("package.json workflowCode.kanban must be an object.");
614
+ }
615
+ return {
616
+ artifactDir: readKanbanRelativePath(
617
+ rawConfig?.["artifactDir"],
618
+ "workflowCode.kanban.artifactDir",
619
+ KANBAN_ARTIFACT_DIR,
620
+ true
621
+ ),
622
+ entry: readKanbanRelativePath(
623
+ rawConfig?.["entry"],
624
+ "workflowCode.kanban.entry",
625
+ KANBAN_ENTRY_FILE,
626
+ false
627
+ )
628
+ };
629
+ }
630
+ function createKanbanArtifactEntryPath(config) {
631
+ return config.artifactDir === KANBAN_ARTIFACT_DIR ? config.entry : `${config.artifactDir}/${config.entry}`;
632
+ }
633
+ function readKanbanRelativePath(value, field, fallback, allowDot) {
634
+ const raw = value === void 0 ? fallback : value;
635
+ if (typeof raw !== "string" || raw.trim() === "") {
636
+ throw new Error(`package.json ${field} must be a non-empty string.`);
637
+ }
638
+ const normalized = raw.trim().replaceAll("\\", "/").replace(/\/+$/, "");
639
+ if (allowDot && normalized === ".") return normalized;
640
+ if (normalized === "" || normalized === "." || normalized.startsWith("/") || normalized.startsWith("//") || /^[a-zA-Z]:/.test(normalized) || normalized.includes("\0") || normalized.split("/").some((part) => part === "" || part === "." || part === "..")) {
641
+ throw new Error(`package.json ${field} must be a safe project-relative path.`);
642
+ }
643
+ const reservedDirectory = normalized.split("/").find((part) => [".git", ".hg", ".svn", "node_modules"].includes(part.toLowerCase()));
644
+ if (reservedDirectory !== void 0) {
645
+ throw new Error(
646
+ `package.json ${field} must not use reserved directory ${JSON.stringify(reservedDirectory)}.`
647
+ );
648
+ }
649
+ if (!allowDot && !/\.html?$/i.test(normalized)) {
650
+ throw new Error(`package.json ${field} must point to an HTML file.`);
651
+ }
652
+ return normalized;
653
+ }
654
+ function isRecord2(value) {
655
+ return typeof value === "object" && value !== null && !Array.isArray(value);
656
+ }
657
+
658
+ // ../../shared/project-upload/index.ts
659
+ var WORKFLOW_IGNORE_FILE = ".workflowignore";
660
+ function hasWorkflowIgnoreRules(content) {
661
+ if (content === void 0) return false;
662
+ return content.split(/\r?\n/).some((line) => {
663
+ const trimmed = line.trim();
664
+ return trimmed !== "" && !trimmed.startsWith("#");
665
+ });
666
+ }
667
+ function resolveWorkflowUploadRequiredPaths(packageJson, availablePaths) {
668
+ const normalizedAvailablePaths = new Set(
669
+ Array.from(availablePaths, (filePath) => normalizeWorkflowUploadPath(filePath))
670
+ );
671
+ if (readPackageProjectType(packageJson) === "kanban") {
672
+ const artifact = readKanbanArtifactConfig(packageJson);
673
+ return [
674
+ "package.json",
675
+ createKanbanArtifactEntryPath(artifact),
676
+ ...normalizedAvailablePaths.has(KANBAN_MANIFEST_FILE) ? [KANBAN_MANIFEST_FILE] : []
677
+ ];
678
+ }
679
+ return ["package.json", "index.ts"];
680
+ }
681
+ function createWorkflowUploadMatcher(content) {
682
+ const ruleLines = content === void 0 ? [] : content.split(/\r?\n/);
683
+ const matcher = (0, import_ignore.default)().add(ruleLines);
684
+ return {
685
+ ignores(relativePath, options) {
686
+ const normalized = normalizeWorkflowUploadPath(relativePath, options);
687
+ return normalized === WORKFLOW_IGNORE_FILE ? false : matcher.ignores(normalized);
688
+ },
689
+ explain(relativePath, options) {
690
+ const normalized = normalizeWorkflowUploadPath(relativePath, options);
691
+ if (normalized === WORKFLOW_IGNORE_FILE) return void 0;
692
+ return findEffectiveWorkflowIgnoreRule(normalized, ruleLines);
693
+ },
694
+ assertRequiredPaths(relativePaths) {
695
+ for (const relativePath of relativePaths) {
696
+ const normalized = normalizeWorkflowUploadPath(relativePath);
697
+ if (!matcher.ignores(normalized)) continue;
698
+ const rule = findEffectiveWorkflowIgnoreRule(normalized, ruleLines);
699
+ const suffix = rule === void 0 ? "" : ` by rule ${JSON.stringify(rule)}`;
700
+ throw new Error(
701
+ `Required upload path ${JSON.stringify(normalized)} is excluded by ${WORKFLOW_IGNORE_FILE}${suffix}.`
702
+ );
703
+ }
704
+ }
705
+ };
706
+ }
707
+ function normalizeWorkflowUploadPath(relativePath, options = {}) {
708
+ const normalized = relativePath.replaceAll("\\", "/").replace(/^\.\/+/, "");
709
+ const withoutTrailingSlash = normalized.replace(/\/+$/, "");
710
+ if (withoutTrailingSlash === "" || withoutTrailingSlash.startsWith("/") || /^[a-zA-Z]:/.test(withoutTrailingSlash) || withoutTrailingSlash.includes("\0") || withoutTrailingSlash.split("/").some((part) => part === "" || part === "." || part === "..")) {
711
+ throw new Error(`Invalid workflow upload path ${JSON.stringify(relativePath)}.`);
712
+ }
713
+ return options.directory === true ? `${withoutTrailingSlash}/` : withoutTrailingSlash;
714
+ }
715
+ function createWorkflowUploadPortablePathKey(relativePath) {
716
+ return normalizeWorkflowUploadPath(relativePath).normalize("NFC").toLowerCase();
717
+ }
718
+ function findEffectiveWorkflowIgnoreRule(normalizedPath, ruleLines) {
719
+ const matcher = (0, import_ignore.default)();
720
+ let ignored = false;
721
+ let effectiveRule;
722
+ for (const ruleLine of ruleLines) {
723
+ matcher.add(ruleLine);
724
+ const nextIgnored = matcher.ignores(normalizedPath);
725
+ if (nextIgnored !== ignored) {
726
+ ignored = nextIgnored;
727
+ effectiveRule = ruleLine;
728
+ }
729
+ }
730
+ return ignored ? effectiveRule : void 0;
731
+ }
732
+
733
+ // ../../shared/project-upload/node.ts
734
+ async function createWorkflowUploadPlan(rootDir) {
735
+ const projectRoot = path2.resolve(rootDir);
736
+ const workflowIgnorePath = path2.join(projectRoot, WORKFLOW_IGNORE_FILE);
737
+ const workflowIgnoreStat = await lstat(workflowIgnorePath).catch(() => void 0);
738
+ if (workflowIgnoreStat?.isSymbolicLink()) {
739
+ throw new Error(`${WORKFLOW_IGNORE_FILE} must be a regular file.`);
740
+ }
741
+ const workflowIgnorePresent = workflowIgnoreStat?.isFile() === true;
742
+ if (workflowIgnoreStat !== void 0 && !workflowIgnorePresent) {
743
+ throw new Error(`${WORKFLOW_IGNORE_FILE} must be a regular file.`);
744
+ }
745
+ const workflowIgnoreContent = workflowIgnorePresent ? await readWorkflowUploadUtf8File(workflowIgnorePath, WORKFLOW_IGNORE_FILE) : void 0;
746
+ const matcher = createWorkflowUploadMatcher(
747
+ workflowIgnoreContent
748
+ );
749
+ const files = [];
750
+ const availableRegularPaths = /* @__PURE__ */ new Set();
751
+ const portablePaths = /* @__PURE__ */ new Map();
752
+ const ignoredPaths = [];
753
+ const walk = async (currentDir) => {
754
+ const entries = await readdir(currentDir, { withFileTypes: true });
755
+ for (const entry of entries) {
756
+ const absolutePath = path2.join(currentDir, entry.name);
757
+ const relativePath = normalizeWorkflowUploadPath(path2.relative(projectRoot, absolutePath));
758
+ const portablePath = createWorkflowUploadPortablePathKey(relativePath);
759
+ const conflictingPath = portablePaths.get(portablePath);
760
+ if (conflictingPath !== void 0 && conflictingPath !== relativePath) {
761
+ throw new Error(
762
+ `Workflow upload paths ${JSON.stringify(conflictingPath)} and ${JSON.stringify(relativePath)} conflict on a case-insensitive filesystem.`
763
+ );
764
+ }
765
+ portablePaths.set(portablePath, relativePath);
766
+ if (entry.isDirectory()) {
767
+ if (matcher.ignores(relativePath, { directory: true })) {
768
+ ignoredPaths.push(`${relativePath}/`);
769
+ continue;
770
+ }
771
+ await walk(absolutePath);
772
+ continue;
773
+ }
774
+ if (!entry.isFile()) {
775
+ throw new Error(`Workflow upload path ${JSON.stringify(relativePath)} must be a regular file or directory.`);
776
+ }
777
+ availableRegularPaths.add(relativePath);
778
+ if (matcher.ignores(relativePath)) {
779
+ ignoredPaths.push(relativePath);
780
+ continue;
781
+ }
782
+ const fileStat = await lstat(absolutePath);
783
+ files.push({ path: relativePath, absolutePath, size: fileStat.size });
784
+ }
785
+ };
786
+ await walk(projectRoot);
787
+ files.sort((left, right) => left.path.localeCompare(right.path));
788
+ ignoredPaths.sort((left, right) => left.localeCompare(right));
789
+ if (!availableRegularPaths.has("package.json")) {
790
+ throw new Error("Workflow package must contain package.json.");
791
+ }
792
+ const packageJson = parsePackageJson(await readWorkflowUploadUtf8File(
793
+ path2.join(projectRoot, "package.json"),
794
+ "package.json"
795
+ ));
796
+ const requiredPaths = resolveWorkflowUploadRequiredPaths(
797
+ packageJson,
798
+ availableRegularPaths
799
+ );
800
+ matcher.assertRequiredPaths(requiredPaths);
801
+ for (const requiredPath of requiredPaths) {
802
+ if (!availableRegularPaths.has(requiredPath)) {
803
+ throw new Error(`Workflow package must contain required upload path ${JSON.stringify(requiredPath)}.`);
804
+ }
805
+ }
806
+ return {
807
+ workflowIgnorePresent,
808
+ workflowIgnoreConfigured: hasWorkflowIgnoreRules(workflowIgnoreContent),
809
+ files,
810
+ ignoredPaths,
811
+ totalBytes: files.reduce((total, file) => total + file.size, 0),
812
+ requiredPaths
813
+ };
814
+ }
815
+ async function copyWorkflowUploadFiles(sourceDir, targetDir) {
816
+ const plan = await createWorkflowUploadPlan(sourceDir);
817
+ await mkdir(targetDir, { recursive: true });
818
+ for (const file of plan.files) {
819
+ const targetPath = path2.join(targetDir, ...file.path.split("/"));
820
+ await mkdir(path2.dirname(targetPath), { recursive: true });
821
+ await copyFile(file.absolutePath, targetPath);
822
+ }
823
+ return plan;
824
+ }
825
+ function parsePackageJson(content) {
826
+ try {
827
+ return JSON.parse(content);
828
+ } catch {
829
+ throw new Error("Workflow package.json is invalid JSON.");
830
+ }
831
+ }
832
+ async function readWorkflowUploadUtf8File(filePath, label) {
833
+ const content = await readFile(filePath);
834
+ try {
835
+ return new TextDecoder("utf-8", { fatal: true }).decode(content);
836
+ } catch {
837
+ throw new Error(`Workflow ${label} must be a UTF-8 text file.`);
838
+ }
839
+ }
840
+
123
841
  // src/auth-config.ts
124
842
  import { readFileSync } from "fs";
125
- import { mkdir, readFile, rm, writeFile } from "fs/promises";
843
+ import { mkdir as mkdir2, readFile as readFile2, rm, writeFile } from "fs/promises";
126
844
  async function readCliAuthConfig(env = process.env) {
127
845
  try {
128
- const raw = await readFile(resolveWorkflowCliAuthFilePath(env), "utf8");
846
+ const raw = await readFile2(resolveWorkflowCliAuthFilePath(env), "utf8");
129
847
  return normalizeWorkflowAuthConfig(JSON.parse(raw));
130
848
  } catch {
131
849
  return createEmptyWorkflowAuthConfig();
@@ -140,7 +858,7 @@ function readCliAuthConfigSync(env = process.env) {
140
858
  }
141
859
  }
142
860
  async function writeCliAuthConfig(config, env = process.env) {
143
- await mkdir(resolveWorkflowCliAuthDir(env), { recursive: true });
861
+ await mkdir2(resolveWorkflowCliAuthDir(env), { recursive: true });
144
862
  await writeFile(
145
863
  resolveWorkflowCliAuthFilePath(env),
146
864
  `${JSON.stringify(normalizeWorkflowAuthConfig(config), null, 2)}
@@ -154,28 +872,28 @@ async function clearCliAuthConfig(env = process.env) {
154
872
 
155
873
  // src/env.ts
156
874
  import { existsSync, readFileSync as readFileSync2 } from "fs";
157
- import path2 from "path";
875
+ import path3 from "path";
158
876
  import { parse } from "dotenv";
159
877
  function loadCliEnv() {
160
878
  const cliRoot = process.cwd();
161
879
  const defaultRepoRoot = findRepoRoot(cliRoot) ?? cliRoot;
162
- const initialRepoRoot = process.env.WORKFLOW_REPO_ROOT ? path2.resolve(process.env.WORKFLOW_REPO_ROOT) : defaultRepoRoot;
880
+ const initialRepoRoot = process.env.WORKFLOW_REPO_ROOT ? path3.resolve(process.env.WORKFLOW_REPO_ROOT) : defaultRepoRoot;
163
881
  loadEnvFiles([
164
- path2.join(cliRoot, ".env"),
165
- path2.join(initialRepoRoot, ".env")
882
+ path3.join(cliRoot, ".env"),
883
+ path3.join(initialRepoRoot, ".env")
166
884
  ]);
167
- const resolvedRepoRoot = process.env.WORKFLOW_REPO_ROOT ? path2.resolve(process.env.WORKFLOW_REPO_ROOT) : initialRepoRoot;
885
+ const resolvedRepoRoot = process.env.WORKFLOW_REPO_ROOT ? path3.resolve(process.env.WORKFLOW_REPO_ROOT) : initialRepoRoot;
168
886
  if (resolvedRepoRoot !== initialRepoRoot) {
169
- loadEnvFiles([path2.join(resolvedRepoRoot, ".env")]);
887
+ loadEnvFiles([path3.join(resolvedRepoRoot, ".env")]);
170
888
  }
171
889
  }
172
890
  function findRepoRoot(startDir) {
173
- let current = path2.resolve(startDir);
891
+ let current = path3.resolve(startDir);
174
892
  while (true) {
175
- if (existsSync(path2.join(current, "pnpm-workspace.yaml")) && existsSync(path2.join(current, "packages", "cli", "package.json"))) {
893
+ if (existsSync(path3.join(current, "pnpm-workspace.yaml")) && existsSync(path3.join(current, "packages", "cli", "package.json"))) {
176
894
  return current;
177
895
  }
178
- const parent = path2.dirname(current);
896
+ const parent = path3.dirname(current);
179
897
  if (parent === current) return void 0;
180
898
  current = parent;
181
899
  }
@@ -199,7 +917,7 @@ function unique(values) {
199
917
 
200
918
  // src/workspace.ts
201
919
  var DEFAULT_SERVER_URL = "http://localhost:7125";
202
- var CLI_MODULE_DIR = path3.dirname(fileURLToPath(import.meta.url));
920
+ var CLI_MODULE_DIR = path4.dirname(fileURLToPath(import.meta.url));
203
921
  var DEFAULT_DEVICE_FLOW_CLIENT_NAME = "Workflow Workspace CLI";
204
922
  loadCliEnv();
205
923
  async function main(argv = process.argv.slice(2)) {
@@ -349,7 +1067,7 @@ async function commandUpload(parsed) {
349
1067
  if (!workflowId) {
350
1068
  throw new Error("Workflow package.json must include a UUID id. Use `workflow-code workspace upload <workflow> --create` after login to create and bind a server project.");
351
1069
  }
352
- const archivePath = parsed.options.file ? path3.resolve(parsed.options.file) : await packWorkflow(workflowArg, parsed.options.output, parsed.options.path);
1070
+ const archivePath = parsed.options.file ? path4.resolve(parsed.options.file) : await packWorkflow(workflowArg, parsed.options.output, parsed.options.path);
353
1071
  const uploadResult = await uploadPackage({
354
1072
  workflowId,
355
1073
  archivePath,
@@ -458,7 +1176,7 @@ async function commandDownload(parsed) {
458
1176
  const result = await fetchApi({
459
1177
  server: parsed.options.server,
460
1178
  token: parsed.options.token,
461
- path: `/api/workflows/${encodeURIComponent(workflowId)}/files?target=${encodeURIComponent(target)}`
1179
+ path: `/api/workflows/${encodeURIComponent(workflowId)}/files?target=${encodeURIComponent(target)}&includeBinary=true`
462
1180
  });
463
1181
  printApiResponse(result);
464
1182
  assertApiOk(result);
@@ -466,14 +1184,14 @@ async function commandDownload(parsed) {
466
1184
  return;
467
1185
  }
468
1186
  const repoRoot = resolveRepoRoot();
469
- const targetDir = parsed.options.path ? path3.resolve(parsed.options.path) : path3.join(repoRoot, "workspace", "workflow", workflowArg);
1187
+ const targetDir = parsed.options.path ? path4.resolve(parsed.options.path) : path4.join(repoRoot, "workspace", "workflow", workflowArg);
470
1188
  await rm2(targetDir, { recursive: true, force: true });
471
- await mkdir2(targetDir, { recursive: true });
1189
+ await mkdir3(targetDir, { recursive: true });
472
1190
  for (const file of result.data.files) {
473
1191
  const safePath = normalizeWorkflowFilePath(file.path);
474
- const targetPath = path3.join(targetDir, safePath);
475
- await mkdir2(path3.dirname(targetPath), { recursive: true });
476
- await writeFile2(targetPath, file.content);
1192
+ const targetPath = path4.join(targetDir, safePath);
1193
+ await mkdir3(path4.dirname(targetPath), { recursive: true });
1194
+ await writeFile2(targetPath, decodeWorkflowFileContent(file));
477
1195
  }
478
1196
  }
479
1197
  async function commandPublish(parsed) {
@@ -504,27 +1222,25 @@ async function commandHealth(parsed) {
504
1222
  }
505
1223
  async function packWorkflow(workflowName, outputPath, sourcePath) {
506
1224
  const repoRoot = resolveRepoRoot();
507
- const workflowDir = sourcePath === void 0 ? path3.join(repoRoot, "workspace", "workflow", workflowName) : path3.resolve(sourcePath);
508
- if (!existsSync2(path3.join(workflowDir, "README.md"))) {
509
- throw new Error(`Workflow project "${workflowDir}" must contain README.md before upload.`);
510
- }
511
- const packsDir = process.env.WORKFLOW_WORKSPACE_PACKS_DIR ? path3.resolve(process.env.WORKFLOW_WORKSPACE_PACKS_DIR) : path3.join(repoRoot, "workspace", ".packs");
512
- const stagingDir = path3.join(packsDir, "tmp", randomUUID(), workflowName);
513
- const archivePath = outputPath ? path3.resolve(outputPath) : path3.join(packsDir, `${workflowName}-${createTimestamp()}.tgz`);
514
- const workspaceRoot = sourcePath === void 0 ? path3.join(repoRoot, "workspace", "workflow") : path3.dirname(workflowDir);
515
- await rm2(path3.dirname(stagingDir), { recursive: true, force: true });
516
- await mkdir2(path3.join(stagingDir, "workspace", "workflow"), { recursive: true });
517
- await mkdir2(path3.dirname(archivePath), { recursive: true });
1225
+ const workflowDir = sourcePath === void 0 ? path4.join(repoRoot, "workspace", "workflow", workflowName) : path4.resolve(sourcePath);
1226
+ await readPackageJsonFile(path4.join(workflowDir, "package.json"));
1227
+ const packsDir = process.env.WORKFLOW_WORKSPACE_PACKS_DIR ? path4.resolve(process.env.WORKFLOW_WORKSPACE_PACKS_DIR) : path4.join(repoRoot, "workspace", ".packs");
1228
+ const stagingDir = path4.join(packsDir, "tmp", randomUUID(), workflowName);
1229
+ const archivePath = outputPath ? path4.resolve(outputPath) : path4.join(packsDir, `${workflowName}-${createTimestamp()}.tgz`);
1230
+ const workspaceRoot = sourcePath === void 0 ? path4.join(repoRoot, "workspace", "workflow") : path4.dirname(workflowDir);
1231
+ await rm2(path4.dirname(stagingDir), { recursive: true, force: true });
1232
+ await mkdir3(path4.join(stagingDir, "workspace", "workflow"), { recursive: true });
1233
+ await mkdir3(path4.dirname(archivePath), { recursive: true });
518
1234
  await stageWorkflowWithLocalDependencies({
519
1235
  workspaceRoot,
520
1236
  workflowDir,
521
1237
  workflowName,
522
- stagingRoot: path3.join(stagingDir, "workspace", "workflow")
1238
+ stagingRoot: path4.join(stagingDir, "workspace", "workflow")
523
1239
  });
524
1240
  const result = await runCommand("tar", ["-czf", archivePath, "-C", stagingDir, "."], {
525
1241
  cwd: repoRoot
526
1242
  });
527
- await rm2(path3.dirname(stagingDir), { recursive: true, force: true });
1243
+ await rm2(path4.dirname(stagingDir), { recursive: true, force: true });
528
1244
  if (result.exitCode !== 0) {
529
1245
  throw new Error(`Failed to create archive: ${result.stderr || result.stdout}`);
530
1246
  }
@@ -543,8 +1259,8 @@ async function resolveRemoteWorkflowId(parsed) {
543
1259
  }
544
1260
  async function readWorkflowPackageContext(workflowArg, sourcePath) {
545
1261
  const repoRoot = resolveRepoRoot();
546
- const workflowDir = sourcePath === void 0 ? path3.join(repoRoot, "workspace", "workflow", workflowArg) : path3.resolve(sourcePath);
547
- const packageJsonPath = path3.join(workflowDir, "package.json");
1262
+ const workflowDir = sourcePath === void 0 ? path4.join(repoRoot, "workspace", "workflow", workflowArg) : path4.resolve(sourcePath);
1263
+ const packageJsonPath = path4.join(workflowDir, "package.json");
548
1264
  const packageJson = await readPackageJsonFile(packageJsonPath);
549
1265
  const workflowId = typeof packageJson.id === "string" && isWorkflowUuid(packageJson.id) ? packageJson.id.trim() : void 0;
550
1266
  const workflowName = readRequiredPackageString(packageJson, "name");
@@ -580,7 +1296,7 @@ async function ensureWorkflowProjectCreated(options) {
580
1296
  }
581
1297
  async function readPackageJsonFile(packageJsonPath) {
582
1298
  try {
583
- const raw = JSON.parse(await readFile2(packageJsonPath, "utf8"));
1299
+ const raw = JSON.parse(await readFile3(packageJsonPath, "utf8"));
584
1300
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
585
1301
  throw new Error(`Workflow package "${packageJsonPath}" must contain a JSON object.`);
586
1302
  }
@@ -611,8 +1327,8 @@ async function writeWorkflowPackageId(packageJsonPath, workflowId) {
611
1327
  );
612
1328
  }
613
1329
  async function uploadPackage(options) {
614
- const archiveBuffer = await readFile2(options.archivePath);
615
- const archiveName = path3.basename(options.archivePath);
1330
+ const archiveBuffer = await readFile3(options.archivePath);
1331
+ const archiveName = path4.basename(options.archivePath);
616
1332
  const response = await fetch(
617
1333
  `${trimTrailingSlash(options.server)}/api/workflows/${encodeURIComponent(options.workflowId)}/package?fileName=${encodeURIComponent(archiveName)}`,
618
1334
  {
@@ -683,13 +1399,7 @@ async function fetchApi(options) {
683
1399
  return response.json();
684
1400
  }
685
1401
  async function copyWorkflowSource(sourceDir, targetDir) {
686
- await cp(sourceDir, targetDir, {
687
- recursive: true,
688
- filter(source) {
689
- const relative = path3.relative(sourceDir, source);
690
- return relative === "" || !shouldExcludePackagePath(relative);
691
- }
692
- });
1402
+ return copyWorkflowUploadFiles(sourceDir, targetDir);
693
1403
  }
694
1404
  async function stageWorkflowWithLocalDependencies(options) {
695
1405
  const queue = [options.workflowName];
@@ -700,13 +1410,23 @@ async function stageWorkflowWithLocalDependencies(options) {
700
1410
  continue;
701
1411
  }
702
1412
  visited.add(currentWorkflowName);
703
- const sourceDir = currentWorkflowName === options.workflowName ? options.workflowDir : path3.join(options.workspaceRoot, currentWorkflowName);
704
- if (!existsSync2(path3.join(sourceDir, "package.json"))) {
1413
+ const sourceDir = currentWorkflowName === options.workflowName ? options.workflowDir : path4.join(options.workspaceRoot, currentWorkflowName);
1414
+ if (!existsSync2(path4.join(sourceDir, "package.json"))) {
705
1415
  throw new Error(
706
1416
  `Workflow "${options.workflowName}" references local workflow "${currentWorkflowName}", but ${sourceDir} is missing package.json.`
707
1417
  );
708
1418
  }
709
- await copyWorkflowSource(sourceDir, path3.join(options.stagingRoot, currentWorkflowName));
1419
+ const uploadPlan = await copyWorkflowSource(
1420
+ sourceDir,
1421
+ path4.join(options.stagingRoot, currentWorkflowName)
1422
+ );
1423
+ if (currentWorkflowName === options.workflowName && !uploadPlan.workflowIgnoreConfigured) {
1424
+ process.stderr.write([
1425
+ "Warning: .workflowignore is missing or has no active rules; all regular project files will be uploaded.",
1426
+ `.gitignore is not used. Files: ${uploadPlan.files.length}; bytes: ${uploadPlan.totalBytes}.`,
1427
+ ""
1428
+ ].join("\n"));
1429
+ }
710
1430
  for (const dependency of await findLocalWorkflowDependencies(sourceDir)) {
711
1431
  if (!visited.has(dependency)) {
712
1432
  queue.push(dependency);
@@ -717,11 +1437,11 @@ async function stageWorkflowWithLocalDependencies(options) {
717
1437
  async function findLocalWorkflowDependencies(sourceDir) {
718
1438
  const discovered = /* @__PURE__ */ new Set();
719
1439
  for (const fileName of ["index.ts", "interface.ts", "globals.d.ts"]) {
720
- const filePath = path3.join(sourceDir, fileName);
1440
+ const filePath = path4.join(sourceDir, fileName);
721
1441
  if (!existsSync2(filePath)) {
722
1442
  continue;
723
1443
  }
724
- const source = await readFile2(filePath, "utf8");
1444
+ const source = await readFile3(filePath, "utf8");
725
1445
  for (const match of source.matchAll(/(?:from\s+|reference path=)\"(\.\.\/([^/\"']+)(?:\/[^\"']*)?)\"/g)) {
726
1446
  const workflowName = match[2]?.trim();
727
1447
  if (workflowName) {
@@ -737,39 +1457,30 @@ async function findLocalWorkflowDependencies(sourceDir) {
737
1457
  }
738
1458
  return [...discovered].sort((left, right) => left.localeCompare(right));
739
1459
  }
740
- function shouldExcludePackagePath(relativePath) {
741
- const parts = relativePath.split(/[\\/]/);
742
- const name = parts.at(-1) ?? "";
743
- const excludedDirectories = /* @__PURE__ */ new Set([
744
- "node_modules",
745
- "dist",
746
- "build",
747
- "out",
748
- "coverage",
749
- ".git",
750
- ".hg",
751
- ".svn",
752
- ".cache",
753
- ".turbo",
754
- ".next",
755
- ".nuxt",
756
- ".pnpm-store"
757
- ]);
758
- if (parts.some((part) => excludedDirectories.has(part))) {
759
- return true;
760
- }
761
- if (name === ".env" || name.startsWith(".env.")) {
762
- return name !== ".env.example";
763
- }
764
- return name === ".DS_Store" || name === "Thumbs.db" || name.endsWith(".tmp") || name.endsWith(".log") || name.endsWith(".tsbuildinfo") || name.endsWith("~");
765
- }
766
1460
  function normalizeWorkflowFilePath(filePath) {
767
1461
  const normalized = filePath.replaceAll("\\", "/").replace(/^\.\/+/, "");
768
- if (normalized === "" || normalized.startsWith("/") || /^[a-zA-Z]:/.test(normalized) || normalized.split("/").some((part) => part === "" || part === "." || part === "..") || shouldExcludePackagePath(normalized)) {
1462
+ if (normalized === "" || normalized.startsWith("/") || /^[a-zA-Z]:/.test(normalized) || normalized.split("/").some((part) => part === "" || part === "." || part === "..")) {
769
1463
  throw new Error(`Invalid workflow file path "${filePath}".`);
770
1464
  }
771
1465
  return normalized;
772
1466
  }
1467
+ function decodeWorkflowFileContent(file) {
1468
+ if (file.encoding === void 0) {
1469
+ return file.content;
1470
+ }
1471
+ if (file.encoding !== "base64") {
1472
+ throw new Error(`Unsupported encoding for workflow file "${file.path}".`);
1473
+ }
1474
+ const normalized = file.content.replace(/=+$/, "");
1475
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(file.content)) {
1476
+ throw new Error(`Invalid base64 content for workflow file "${file.path}".`);
1477
+ }
1478
+ const decoded = Buffer.from(file.content, "base64");
1479
+ if (decoded.toString("base64").replace(/=+$/, "") !== normalized) {
1480
+ throw new Error(`Invalid base64 content for workflow file "${file.path}".`);
1481
+ }
1482
+ return decoded;
1483
+ }
773
1484
  function parseArgs(argv) {
774
1485
  const [command = "", ...rest] = argv;
775
1486
  const positional = [];
@@ -927,7 +1638,7 @@ function printUsage() {
927
1638
  }
928
1639
  function resolveRepoRoot(startDir = process.cwd()) {
929
1640
  if (process.env.WORKFLOW_REPO_ROOT !== void 0) {
930
- return path3.resolve(process.env.WORKFLOW_REPO_ROOT);
1641
+ return path4.resolve(process.env.WORKFLOW_REPO_ROOT);
931
1642
  }
932
1643
  for (const candidate of [startDir, CLI_MODULE_DIR]) {
933
1644
  const repoRoot = findRepoRoot2(candidate);
@@ -938,12 +1649,12 @@ function resolveRepoRoot(startDir = process.cwd()) {
938
1649
  return process.cwd();
939
1650
  }
940
1651
  function findRepoRoot2(startDir) {
941
- let current = path3.resolve(startDir);
1652
+ let current = path4.resolve(startDir);
942
1653
  while (true) {
943
1654
  if (isRepoRoot(current)) {
944
1655
  return current;
945
1656
  }
946
- const parent = path3.dirname(current);
1657
+ const parent = path4.dirname(current);
947
1658
  if (parent === current) {
948
1659
  return void 0;
949
1660
  }
@@ -951,7 +1662,7 @@ function findRepoRoot2(startDir) {
951
1662
  }
952
1663
  }
953
1664
  function isRepoRoot(candidate) {
954
- return existsSync2(path3.join(candidate, "pnpm-workspace.yaml")) && existsSync2(path3.join(candidate, "package.json")) && existsSync2(path3.join(candidate, "workspace", "package.json")) && existsSync2(path3.join(candidate, "packages", "cli", "package.json"));
1665
+ 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"));
955
1666
  }
956
1667
  function trimTrailingSlash(value) {
957
1668
  return value.replace(/\/+$/, "");
@@ -1018,13 +1729,14 @@ function readRetryAfterSeconds(data) {
1018
1729
  const value = data.retryAfterSeconds;
1019
1730
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
1020
1731
  }
1021
- if (process.argv[1] !== void 0 && import.meta.url === pathToFileURL(path3.resolve(process.argv[1])).href) {
1732
+ if (process.argv[1] !== void 0 && import.meta.url === pathToFileURL(path4.resolve(process.argv[1])).href) {
1022
1733
  main().catch((error) => {
1023
1734
  console.error(error instanceof Error ? error.message : String(error));
1024
1735
  process.exitCode = 1;
1025
1736
  });
1026
1737
  }
1027
1738
  export {
1739
+ decodeWorkflowFileContent,
1028
1740
  ensureWorkflowProjectCreated,
1029
1741
  findLocalWorkflowDependencies,
1030
1742
  formatForbiddenMessage,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workflow-code/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "workflow-code": "./dist/index.js"