windmill-cli 1.681.0 → 1.683.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -0
- package/esm/main.js +887 -162
- package/package.json +1 -1
package/esm/main.js
CHANGED
|
@@ -11812,7 +11812,7 @@ var init_OpenAPI = __esm(() => {
|
|
|
11812
11812
|
PASSWORD: undefined,
|
|
11813
11813
|
TOKEN: getEnv2("WM_TOKEN"),
|
|
11814
11814
|
USERNAME: undefined,
|
|
11815
|
-
VERSION: "1.
|
|
11815
|
+
VERSION: "1.683.0",
|
|
11816
11816
|
WITH_CREDENTIALS: true,
|
|
11817
11817
|
interceptors: {
|
|
11818
11818
|
request: new Interceptors,
|
|
@@ -12350,6 +12350,7 @@ __export(exports_services_gen, {
|
|
|
12350
12350
|
listExtJwtTokens: () => listExtJwtTokens,
|
|
12351
12351
|
listEmailTriggers: () => listEmailTriggers,
|
|
12352
12352
|
listDucklakes: () => listDucklakes,
|
|
12353
|
+
listDeploymentRequestEligibleDeployers: () => listDeploymentRequestEligibleDeployers,
|
|
12353
12354
|
listDedicatedWithDeps: () => listDedicatedWithDeps,
|
|
12354
12355
|
listDataTables: () => listDataTables,
|
|
12355
12356
|
listDataTableSchemas: () => listDataTableSchemas,
|
|
@@ -12434,6 +12435,7 @@ __export(exports_services_gen, {
|
|
|
12434
12435
|
getScheduledFor: () => getScheduledFor,
|
|
12435
12436
|
getSchedule: () => getSchedule,
|
|
12436
12437
|
getRunnable: () => getRunnable,
|
|
12438
|
+
getRuffConfig: () => getRuffConfig,
|
|
12437
12439
|
getRootJobId: () => getRootJobId,
|
|
12438
12440
|
getResumeUrls: () => getResumeUrls,
|
|
12439
12441
|
getResourceValueInterpolated: () => getResourceValueInterpolated,
|
|
@@ -12453,6 +12455,7 @@ __export(exports_services_gen, {
|
|
|
12453
12455
|
getPostgresVersion: () => getPostgresVersion,
|
|
12454
12456
|
getPostgresTrigger: () => getPostgresTrigger,
|
|
12455
12457
|
getPostgresPublication: () => getPostgresPublication,
|
|
12458
|
+
getOpenDeploymentRequest: () => getOpenDeploymentRequest,
|
|
12456
12459
|
getOpenApiYaml: () => getOpenApiYaml,
|
|
12457
12460
|
getOidcToken: () => getOidcToken,
|
|
12458
12461
|
getObjectStorageUsage: () => getObjectStorageUsage,
|
|
@@ -12716,6 +12719,8 @@ __export(exports_services_gen, {
|
|
|
12716
12719
|
createFlow: () => createFlow,
|
|
12717
12720
|
createEmailTrigger: () => createEmailTrigger,
|
|
12718
12721
|
createDraft: () => createDraft,
|
|
12722
|
+
createDeploymentRequestComment: () => createDeploymentRequestComment,
|
|
12723
|
+
createDeploymentRequest: () => createDeploymentRequest,
|
|
12719
12724
|
createCustomerPortalSession: () => createCustomerPortalSession,
|
|
12720
12725
|
createAppRaw: () => createAppRaw,
|
|
12721
12726
|
createApp: () => createApp,
|
|
@@ -12733,6 +12738,7 @@ __export(exports_services_gen, {
|
|
|
12733
12738
|
computeObjectStorageUsage: () => computeObjectStorageUsage,
|
|
12734
12739
|
compareWorkspaces: () => compareWorkspaces,
|
|
12735
12740
|
commitKafkaOffsets: () => commitKafkaOffsets,
|
|
12741
|
+
closeDeploymentRequestMerged: () => closeDeploymentRequestMerged,
|
|
12736
12742
|
clearIndex: () => clearIndex,
|
|
12737
12743
|
checkS3FolderExists: () => checkS3FolderExists,
|
|
12738
12744
|
checkInstanceSharingAvailable: () => checkInstanceSharingAvailable,
|
|
@@ -12746,6 +12752,7 @@ __export(exports_services_gen, {
|
|
|
12746
12752
|
cancelSelection: () => cancelSelection,
|
|
12747
12753
|
cancelQueuedJob: () => cancelQueuedJob,
|
|
12748
12754
|
cancelPersistentQueuedJobs: () => cancelPersistentQueuedJobs,
|
|
12755
|
+
cancelDeploymentRequest: () => cancelDeploymentRequest,
|
|
12749
12756
|
blacklistAgentToken: () => blacklistAgentToken,
|
|
12750
12757
|
batchReRunJobs: () => batchReRunJobs,
|
|
12751
12758
|
backendVersion: () => backendVersion,
|
|
@@ -13191,6 +13198,11 @@ var backendVersion = () => {
|
|
|
13191
13198
|
body: data2.requestBody,
|
|
13192
13199
|
mediaType: "application/json"
|
|
13193
13200
|
});
|
|
13201
|
+
}, getRuffConfig = () => {
|
|
13202
|
+
return request(OpenAPI, {
|
|
13203
|
+
method: "GET",
|
|
13204
|
+
url: "/settings_u/ruff_config"
|
|
13205
|
+
});
|
|
13194
13206
|
}, getLocal = () => {
|
|
13195
13207
|
return request(OpenAPI, {
|
|
13196
13208
|
method: "GET",
|
|
@@ -14544,6 +14556,68 @@ var backendVersion = () => {
|
|
|
14544
14556
|
404: "protection rule not found"
|
|
14545
14557
|
}
|
|
14546
14558
|
});
|
|
14559
|
+
}, listDeploymentRequestEligibleDeployers = (data2) => {
|
|
14560
|
+
return request(OpenAPI, {
|
|
14561
|
+
method: "GET",
|
|
14562
|
+
url: "/w/{workspace}/deployment_request/eligible_deployers",
|
|
14563
|
+
path: {
|
|
14564
|
+
workspace: data2.workspace
|
|
14565
|
+
}
|
|
14566
|
+
});
|
|
14567
|
+
}, getOpenDeploymentRequest = (data2) => {
|
|
14568
|
+
return request(OpenAPI, {
|
|
14569
|
+
method: "GET",
|
|
14570
|
+
url: "/w/{workspace}/deployment_request/open",
|
|
14571
|
+
path: {
|
|
14572
|
+
workspace: data2.workspace
|
|
14573
|
+
}
|
|
14574
|
+
});
|
|
14575
|
+
}, createDeploymentRequest = (data2) => {
|
|
14576
|
+
return request(OpenAPI, {
|
|
14577
|
+
method: "POST",
|
|
14578
|
+
url: "/w/{workspace}/deployment_request",
|
|
14579
|
+
path: {
|
|
14580
|
+
workspace: data2.workspace
|
|
14581
|
+
},
|
|
14582
|
+
body: data2.requestBody,
|
|
14583
|
+
mediaType: "application/json",
|
|
14584
|
+
errors: {
|
|
14585
|
+
400: "invalid assignees",
|
|
14586
|
+
409: "a deployment request is already open for this fork"
|
|
14587
|
+
}
|
|
14588
|
+
});
|
|
14589
|
+
}, cancelDeploymentRequest = (data2) => {
|
|
14590
|
+
return request(OpenAPI, {
|
|
14591
|
+
method: "POST",
|
|
14592
|
+
url: "/w/{workspace}/deployment_request/{id}/cancel",
|
|
14593
|
+
path: {
|
|
14594
|
+
workspace: data2.workspace,
|
|
14595
|
+
id: data2.id
|
|
14596
|
+
}
|
|
14597
|
+
});
|
|
14598
|
+
}, closeDeploymentRequestMerged = (data2) => {
|
|
14599
|
+
return request(OpenAPI, {
|
|
14600
|
+
method: "POST",
|
|
14601
|
+
url: "/w/{workspace}/deployment_request/{id}/close_merged",
|
|
14602
|
+
path: {
|
|
14603
|
+
workspace: data2.workspace,
|
|
14604
|
+
id: data2.id
|
|
14605
|
+
}
|
|
14606
|
+
});
|
|
14607
|
+
}, createDeploymentRequestComment = (data2) => {
|
|
14608
|
+
return request(OpenAPI, {
|
|
14609
|
+
method: "POST",
|
|
14610
|
+
url: "/w/{workspace}/deployment_request/{id}/comment",
|
|
14611
|
+
path: {
|
|
14612
|
+
workspace: data2.workspace,
|
|
14613
|
+
id: data2.id
|
|
14614
|
+
},
|
|
14615
|
+
body: data2.requestBody,
|
|
14616
|
+
mediaType: "application/json",
|
|
14617
|
+
errors: {
|
|
14618
|
+
400: "invalid input or request closed"
|
|
14619
|
+
}
|
|
14620
|
+
});
|
|
14547
14621
|
}, logAiChat = (data2) => {
|
|
14548
14622
|
return request(OpenAPI, {
|
|
14549
14623
|
method: "POST",
|
|
@@ -26322,6 +26396,7 @@ __export(exports_conf, {
|
|
|
26322
26396
|
showDiffs: () => showDiffs,
|
|
26323
26397
|
setShowDiffs: () => setShowDiffs,
|
|
26324
26398
|
readConfigFile: () => readConfigFile,
|
|
26399
|
+
parseSyncBehavior: () => parseSyncBehavior,
|
|
26325
26400
|
mergeConfigWithConfigFile: () => mergeConfigWithConfigFile,
|
|
26326
26401
|
getWorkspaceNames: () => getWorkspaceNames,
|
|
26327
26402
|
getWmillYamlPath: () => getWmillYamlPath,
|
|
@@ -26330,6 +26405,7 @@ __export(exports_conf, {
|
|
|
26330
26405
|
getEffectiveGitBranch: () => getEffectiveGitBranch,
|
|
26331
26406
|
findWorkspaceByGitBranch: () => findWorkspaceByGitBranch,
|
|
26332
26407
|
convertGitBranchesToWorkspaces: () => convertGitBranchesToWorkspaces,
|
|
26408
|
+
SUPPORTED_SYNC_BEHAVIOR_VERSION: () => SUPPORTED_SYNC_BEHAVIOR_VERSION,
|
|
26333
26409
|
GLOBAL_CONFIG_OPT: () => GLOBAL_CONFIG_OPT,
|
|
26334
26410
|
DEFAULT_SYNC_OPTIONS: () => DEFAULT_SYNC_OPTIONS
|
|
26335
26411
|
});
|
|
@@ -26340,6 +26416,13 @@ import { execSync as execSync2 } from "node:child_process";
|
|
|
26340
26416
|
function setShowDiffs(value) {
|
|
26341
26417
|
showDiffs = value;
|
|
26342
26418
|
}
|
|
26419
|
+
function parseSyncBehavior(value) {
|
|
26420
|
+
if (!value && value !== 0)
|
|
26421
|
+
return 0;
|
|
26422
|
+
const s = String(value);
|
|
26423
|
+
const match = s.match(/^v(\d+)$/);
|
|
26424
|
+
return match ? parseInt(match[1], 10) : 0;
|
|
26425
|
+
}
|
|
26343
26426
|
function getGitRepoRoot() {
|
|
26344
26427
|
try {
|
|
26345
26428
|
const result = execSync2("git rev-parse --show-toplevel", {
|
|
@@ -26441,6 +26524,11 @@ async function readConfigFile(opts) {
|
|
|
26441
26524
|
warn("No defaultTs defined in your wmill.yaml. Using 'bun' as default.");
|
|
26442
26525
|
}
|
|
26443
26526
|
setNonDottedPaths(conf?.nonDottedPaths ?? false);
|
|
26527
|
+
const syncBehaviorVersion = parseSyncBehavior(conf?.syncBehavior);
|
|
26528
|
+
if (syncBehaviorVersion > SUPPORTED_SYNC_BEHAVIOR_VERSION) {
|
|
26529
|
+
error(`Your wmill.yaml specifies syncBehavior: ${conf.syncBehavior}, but this CLI only supports up to v${SUPPORTED_SYNC_BEHAVIOR_VERSION}. Run 'wmill upgrade' to update.`);
|
|
26530
|
+
process.exit(1);
|
|
26531
|
+
}
|
|
26444
26532
|
return typeof conf == "object" ? conf : {};
|
|
26445
26533
|
} catch (e) {
|
|
26446
26534
|
if (e instanceof Error && (e.message.includes("overrides") || e.message.includes("Obsolete configuration format"))) {
|
|
@@ -26637,7 +26725,7 @@ function convertGitBranchesToWorkspaces(gitBranches) {
|
|
|
26637
26725
|
}
|
|
26638
26726
|
return workspaces;
|
|
26639
26727
|
}
|
|
26640
|
-
var import_yaml4, showDiffs = false, GLOBAL_CONFIG_OPT, legacyConfigWarned = false, DEFAULT_SYNC_OPTIONS, RESERVED_WORKSPACE_KEYS;
|
|
26728
|
+
var import_yaml4, showDiffs = false, SUPPORTED_SYNC_BEHAVIOR_VERSION = 1, GLOBAL_CONFIG_OPT, legacyConfigWarned = false, DEFAULT_SYNC_OPTIONS, RESERVED_WORKSPACE_KEYS;
|
|
26641
26729
|
var init_conf = __esm(async () => {
|
|
26642
26730
|
init_log();
|
|
26643
26731
|
init_yaml();
|
|
@@ -26666,7 +26754,8 @@ var init_conf = __esm(async () => {
|
|
|
26666
26754
|
includeSettings: false,
|
|
26667
26755
|
includeKey: false,
|
|
26668
26756
|
skipWorkspaceDependencies: false,
|
|
26669
|
-
nonDottedPaths: false
|
|
26757
|
+
nonDottedPaths: false,
|
|
26758
|
+
syncBehavior: "v1"
|
|
26670
26759
|
};
|
|
26671
26760
|
RESERVED_WORKSPACE_KEYS = new Set(["commonSpecificItems"]);
|
|
26672
26761
|
});
|
|
@@ -33910,6 +33999,22 @@ var escape2 = (s, { windowsPathsNoEscape = false, magicalBraces = false } = {})
|
|
|
33910
33999
|
};
|
|
33911
34000
|
|
|
33912
34001
|
// node_modules/minimatch/dist/esm/index.js
|
|
34002
|
+
var exports_esm = {};
|
|
34003
|
+
__export(exports_esm, {
|
|
34004
|
+
unescape: () => unescape2,
|
|
34005
|
+
sep: () => sep2,
|
|
34006
|
+
minimatch: () => minimatch,
|
|
34007
|
+
match: () => match,
|
|
34008
|
+
makeRe: () => makeRe,
|
|
34009
|
+
filter: () => filter,
|
|
34010
|
+
escape: () => escape2,
|
|
34011
|
+
defaults: () => defaults,
|
|
34012
|
+
braceExpand: () => braceExpand,
|
|
34013
|
+
Minimatch: () => Minimatch,
|
|
34014
|
+
GLOBSTAR: () => GLOBSTAR,
|
|
34015
|
+
AST: () => AST
|
|
34016
|
+
});
|
|
34017
|
+
|
|
33913
34018
|
class Minimatch {
|
|
33914
34019
|
options;
|
|
33915
34020
|
set;
|
|
@@ -60930,17 +61035,17 @@ async function findResourceFile(path7) {
|
|
|
60930
61035
|
}
|
|
60931
61036
|
return validCandidates[0];
|
|
60932
61037
|
}
|
|
60933
|
-
async function handleScriptMetadata(path7, workspace, alreadySynced, message, rawWorkspaceDependencies, codebases, opts) {
|
|
61038
|
+
async function handleScriptMetadata(path7, workspace, alreadySynced, message, rawWorkspaceDependencies, codebases, opts, permissionedAsContext) {
|
|
60934
61039
|
const isFlatMeta = path7.endsWith(".script.json") || path7.endsWith(".script.yaml") || path7.endsWith(".script.lock");
|
|
60935
61040
|
const isFolderMeta = !isFlatMeta && isScriptModulePath(path7) && (path7.endsWith("/script.yaml") || path7.endsWith("/script.json") || path7.endsWith("/script.lock"));
|
|
60936
61041
|
if (isFlatMeta || isFolderMeta) {
|
|
60937
61042
|
const contentPath = await findContentFile(path7);
|
|
60938
|
-
return handleFile(contentPath, workspace, alreadySynced, message, opts, rawWorkspaceDependencies, codebases);
|
|
61043
|
+
return handleFile(contentPath, workspace, alreadySynced, message, opts, rawWorkspaceDependencies, codebases, permissionedAsContext);
|
|
60939
61044
|
} else {
|
|
60940
61045
|
return false;
|
|
60941
61046
|
}
|
|
60942
61047
|
}
|
|
60943
|
-
async function handleFile(path7, workspace, alreadySynced, message, opts, rawWorkspaceDependencies, codebases) {
|
|
61048
|
+
async function handleFile(path7, workspace, alreadySynced, message, opts, rawWorkspaceDependencies, codebases, permissionedAsContext) {
|
|
60944
61049
|
const moduleEntryPoint = isModuleEntryPoint(path7);
|
|
60945
61050
|
if (!isAppInlineScriptPath2(path7) && !isFlowInlineScriptPath2(path7) && !isRawAppBackendPath2(path7) && (!isScriptModulePath(path7) || moduleEntryPoint) && exts.some((exts) => path7.endsWith(exts))) {
|
|
60946
61051
|
if (alreadySynced.includes(path7)) {
|
|
@@ -61085,10 +61190,19 @@ async function handleFile(path7, workspace, alreadySynced, message, opts, rawWor
|
|
|
61085
61190
|
modules,
|
|
61086
61191
|
labels: typed?.labels
|
|
61087
61192
|
};
|
|
61193
|
+
const hasOnBehalfOf = typed?.has_on_behalf_of ?? !!typed?.on_behalf_of_email;
|
|
61194
|
+
delete typed?.has_on_behalf_of;
|
|
61195
|
+
if (permissionedAsContext?.userIsAdminOrDeployer && hasOnBehalfOf) {
|
|
61196
|
+
if (remote && remote.on_behalf_of_email) {
|
|
61197
|
+
requestBodyCommon.on_behalf_of_email = remote.on_behalf_of_email;
|
|
61198
|
+
requestBodyCommon.preserve_on_behalf_of = true;
|
|
61199
|
+
info(`Preserving ${remote.on_behalf_of_email} as on_behalf_of for script ${remotePath}`);
|
|
61200
|
+
}
|
|
61201
|
+
}
|
|
61088
61202
|
if (remote) {
|
|
61089
61203
|
if (content === remote.content) {
|
|
61090
61204
|
if (typed == undefined || typed.description === remote.description && typed.summary === remote.summary && typed.kind == remote.kind && !remote.archived && (Array.isArray(remote?.lock) ? remote?.lock?.join(`
|
|
61091
|
-
`) : remote?.lock ?? "").trim() == (typed?.lock ?? "").trim() && deepEqual(typed.schema, remote.schema) && typed.tag == remote.tag && (typed.ws_error_handler_muted ?? false) == remote.ws_error_handler_muted && typed.dedicated_worker == remote.dedicated_worker && typed.cache_ttl == remote.cache_ttl && typed.concurrency_time_window_s == remote.concurrency_time_window_s && typed.concurrent_limit == remote.concurrent_limit && Boolean(typed.restart_unless_cancelled) == Boolean(remote.restart_unless_cancelled) && Boolean(typed.visible_to_runner_only) == Boolean(remote.visible_to_runner_only) && Boolean(typed.has_preprocessor) == Boolean(remote.has_preprocessor) && typed.priority == Boolean(remote.priority) && typed.timeout == remote.timeout && typed.concurrency_key == remote["concurrency_key"] && typed.debounce_key == remote["debounce_key"] && typed.debounce_delay_s == remote["debounce_delay_s"] && typed.codebase == remote.codebase && typed.on_behalf_of_email == remote.on_behalf_of_email && deepEqual(typed.envs, remote.envs) && deepEqual(modules ?? null, remote.modules ?? null)) {
|
|
61205
|
+
`) : remote?.lock ?? "").trim() == (typed?.lock ?? "").trim() && deepEqual(typed.schema, remote.schema) && typed.tag == remote.tag && (typed.ws_error_handler_muted ?? false) == remote.ws_error_handler_muted && typed.dedicated_worker == remote.dedicated_worker && typed.cache_ttl == remote.cache_ttl && typed.concurrency_time_window_s == remote.concurrency_time_window_s && typed.concurrent_limit == remote.concurrent_limit && Boolean(typed.restart_unless_cancelled) == Boolean(remote.restart_unless_cancelled) && Boolean(typed.visible_to_runner_only) == Boolean(remote.visible_to_runner_only) && Boolean(typed.has_preprocessor) == Boolean(remote.has_preprocessor) && typed.priority == Boolean(remote.priority) && typed.timeout == remote.timeout && typed.concurrency_key == remote["concurrency_key"] && typed.debounce_key == remote["debounce_key"] && typed.debounce_delay_s == remote["debounce_delay_s"] && typed.codebase == remote.codebase && (hasOnBehalfOf ? true : typed.on_behalf_of_email == remote.on_behalf_of_email) && deepEqual(typed.envs, remote.envs) && deepEqual(modules ?? null, remote.modules ?? null)) {
|
|
61092
61206
|
info(colors.green(`Script ${remotePath} is up to date`));
|
|
61093
61207
|
return true;
|
|
61094
61208
|
}
|
|
@@ -61805,6 +61919,28 @@ async function history(opts, scriptPath) {
|
|
|
61805
61919
|
])).render();
|
|
61806
61920
|
}
|
|
61807
61921
|
}
|
|
61922
|
+
async function setPermissionedAs(opts, scriptPath, email) {
|
|
61923
|
+
const workspace = await resolveWorkspace(opts);
|
|
61924
|
+
await requireLogin(opts);
|
|
61925
|
+
const remote = await getScriptByPath({
|
|
61926
|
+
workspace: workspace.workspaceId,
|
|
61927
|
+
path: scriptPath
|
|
61928
|
+
});
|
|
61929
|
+
if (!remote)
|
|
61930
|
+
throw new Error(`Script ${scriptPath} not found`);
|
|
61931
|
+
await createScript({
|
|
61932
|
+
workspace: workspace.workspaceId,
|
|
61933
|
+
requestBody: {
|
|
61934
|
+
...remote,
|
|
61935
|
+
lock: Array.isArray(remote.lock) ? remote.lock.join(`
|
|
61936
|
+
`) : remote.lock ?? undefined,
|
|
61937
|
+
parent_hash: remote.hash,
|
|
61938
|
+
on_behalf_of_email: email,
|
|
61939
|
+
preserve_on_behalf_of: true
|
|
61940
|
+
}
|
|
61941
|
+
});
|
|
61942
|
+
info(colors.green(`Updated permissioned_as for script ${scriptPath} to ${email}`));
|
|
61943
|
+
}
|
|
61808
61944
|
var import_yaml6, exts, POLL_INTERVAL_MS = 2000, languageAliases, command4, script_default;
|
|
61809
61945
|
var init_script = __esm(async () => {
|
|
61810
61946
|
init_colors2();
|
|
@@ -61862,7 +61998,7 @@ var init_script = __esm(async () => {
|
|
|
61862
61998
|
languageAliases = {
|
|
61863
61999
|
python: "python3"
|
|
61864
62000
|
};
|
|
61865
|
-
command4 = new Command().description("script related commands").option("--show-archived", "Show archived scripts instead of active ones").option("--json", "Output as JSON (for piping to jq)").action(list4).command("list", "list all scripts").option("--show-archived", "Show archived scripts instead of active ones").option("--json", "Output as JSON (for piping to jq)").action(list4).command("push", "push a local script spec. This overrides any remote versions. Use the script file (.ts, .js, .py, .sh)").arguments("<path:file>").option("--message <message:string>", "Deployment message").action(push2).command("get", "get a script's details").arguments("<path:file>").option("--json", "Output as JSON (for piping to jq)").action(get2).command("show", "show a script's content (alias for get)").arguments("<path:file>").action(show).command("run", "run a script by path").arguments("<path:file>").option("-d --data <data:file>", "Inputs specified as a JSON string or a file using @<filename> or stdin using @-.").option("-s --silent", "Do not output anything other then the final output. Useful for scripting.").action(run2).command("preview", "preview a local script without deploying it. Supports both regular and codebase scripts.").arguments("<path:file>").option("-d --data <data:file>", "Inputs specified as a JSON string or a file using @<filename> or stdin using @-.").option("-s --silent", "Do not output anything other than the final output. Useful for scripting.").action(preview).command("new", "create a new script").arguments("<path:file> <language:string>").option("--summary <summary:string>", "script summary").option("--description <description:string>", "script description").action(bootstrap).command("bootstrap", "create a new script (alias for new)").arguments("<path:file> <language:string>").option("--summary <summary:string>", "script summary").option("--description <description:string>", "script description").action(bootstrap).command("generate-metadata", 'DEPRECATED: re-generate script metadata. Use top-level "wmill generate-metadata" instead.').arguments("[script:file]").option("--yes", "Skip confirmation prompt").option("--dry-run", "Perform a dry run without making changes").option("--lock-only", "re-generate only the lock").option("--schema-only", "re-generate only script schema").option("-i --includes <patterns:file[]>", "Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string)").option("-e --excludes <patterns:file[]>", "Comma separated patterns to specify which file to NOT take into account.").action(generateMetadata).command("history", "show version history for a script").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(history);
|
|
62001
|
+
command4 = new Command().description("script related commands").option("--show-archived", "Show archived scripts instead of active ones").option("--json", "Output as JSON (for piping to jq)").action(list4).command("list", "list all scripts").option("--show-archived", "Show archived scripts instead of active ones").option("--json", "Output as JSON (for piping to jq)").action(list4).command("push", "push a local script spec. This overrides any remote versions. Use the script file (.ts, .js, .py, .sh)").arguments("<path:file>").option("--message <message:string>", "Deployment message").action(push2).command("get", "get a script's details").arguments("<path:file>").option("--json", "Output as JSON (for piping to jq)").action(get2).command("show", "show a script's content (alias for get)").arguments("<path:file>").action(show).command("run", "run a script by path").arguments("<path:file>").option("-d --data <data:file>", "Inputs specified as a JSON string or a file using @<filename> or stdin using @-.").option("-s --silent", "Do not output anything other then the final output. Useful for scripting.").action(run2).command("preview", "preview a local script without deploying it. Supports both regular and codebase scripts.").arguments("<path:file>").option("-d --data <data:file>", "Inputs specified as a JSON string or a file using @<filename> or stdin using @-.").option("-s --silent", "Do not output anything other than the final output. Useful for scripting.").action(preview).command("new", "create a new script").arguments("<path:file> <language:string>").option("--summary <summary:string>", "script summary").option("--description <description:string>", "script description").action(bootstrap).command("bootstrap", "create a new script (alias for new)").arguments("<path:file> <language:string>").option("--summary <summary:string>", "script summary").option("--description <description:string>", "script description").action(bootstrap).command("generate-metadata", 'DEPRECATED: re-generate script metadata. Use top-level "wmill generate-metadata" instead.').arguments("[script:file]").option("--yes", "Skip confirmation prompt").option("--dry-run", "Perform a dry run without making changes").option("--lock-only", "re-generate only the lock").option("--schema-only", "re-generate only script schema").option("-i --includes <patterns:file[]>", "Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string)").option("-e --excludes <patterns:file[]>", "Comma separated patterns to specify which file to NOT take into account.").action(generateMetadata).command("set-permissioned-as", "Set the on_behalf_of_email for a script (requires admin or wm_deployers group)").arguments("<path:string> <email:string>").action(setPermissionedAs).command("history", "show version history for a script").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(history);
|
|
61866
62002
|
script_default = command4;
|
|
61867
62003
|
});
|
|
61868
62004
|
|
|
@@ -62420,6 +62556,154 @@ var init_lint = __esm(async () => {
|
|
|
62420
62556
|
lint_default = command5;
|
|
62421
62557
|
});
|
|
62422
62558
|
|
|
62559
|
+
// src/core/permissioned_as.ts
|
|
62560
|
+
var exports_permissioned_as = {};
|
|
62561
|
+
__export(exports_permissioned_as, {
|
|
62562
|
+
preCheckPermissionedAs: () => preCheckPermissionedAs,
|
|
62563
|
+
lookupUsernameByEmail: () => lookupUsernameByEmail
|
|
62564
|
+
});
|
|
62565
|
+
async function ensureUserCache(workspace, cache3) {
|
|
62566
|
+
if (cache3.size > 0)
|
|
62567
|
+
return;
|
|
62568
|
+
const users = await listUsers({ workspace });
|
|
62569
|
+
for (const user of users) {
|
|
62570
|
+
cache3.set(user.username, { username: user.username, email: user.email });
|
|
62571
|
+
cache3.set(user.email, { username: user.username, email: user.email });
|
|
62572
|
+
}
|
|
62573
|
+
}
|
|
62574
|
+
async function lookupUsernameByEmail(workspace, email, cache3) {
|
|
62575
|
+
await ensureUserCache(workspace, cache3);
|
|
62576
|
+
const entry = cache3.get(email);
|
|
62577
|
+
if (!entry) {
|
|
62578
|
+
throw new Error(`Could not find username for email '${email}' in workspace. ` + `Make sure the user exists in the workspace.`);
|
|
62579
|
+
}
|
|
62580
|
+
return entry.username;
|
|
62581
|
+
}
|
|
62582
|
+
function contentHasOnBehalfOf(content, typeStr) {
|
|
62583
|
+
if (typeStr === "script") {
|
|
62584
|
+
return !!content.match(/has_on_behalf_of:\s*(true)/) || !!content.match(/on_behalf_of_email:\s*["']?([^\s"']+)["']?/);
|
|
62585
|
+
}
|
|
62586
|
+
if (typeStr === "flow") {
|
|
62587
|
+
return !!content.match(/has_on_behalf_of:\s*(true)/);
|
|
62588
|
+
}
|
|
62589
|
+
return false;
|
|
62590
|
+
}
|
|
62591
|
+
async function preCheckPermissionedAs(changes, userEmail, userIsAdminOrDeployer, acceptOverride, isInteractive) {
|
|
62592
|
+
if (userIsAdminOrDeployer)
|
|
62593
|
+
return;
|
|
62594
|
+
const wouldChangeItems = [];
|
|
62595
|
+
for (const change of changes) {
|
|
62596
|
+
let typeStr;
|
|
62597
|
+
try {
|
|
62598
|
+
typeStr = getTypeStrFromPath(change.path);
|
|
62599
|
+
} catch {
|
|
62600
|
+
continue;
|
|
62601
|
+
}
|
|
62602
|
+
if (change.name === "added") {
|
|
62603
|
+
const content = change.content;
|
|
62604
|
+
if (!content)
|
|
62605
|
+
continue;
|
|
62606
|
+
const isScriptMeta = typeStr === "script" && (change.path.endsWith(".script.yaml") || change.path.endsWith(".script.json"));
|
|
62607
|
+
const isFlowMeta = typeStr === "flow" && (change.path.endsWith("flow.yaml") || change.path.endsWith("flow.json"));
|
|
62608
|
+
if ((isScriptMeta || isFlowMeta) && contentHasOnBehalfOf(content, typeStr)) {
|
|
62609
|
+
const label = typeStr === "script" ? "(script owner)" : "(flow owner)";
|
|
62610
|
+
wouldChangeItems.push({ path: change.path, currentOwner: label });
|
|
62611
|
+
} else if (typeStr === "app") {
|
|
62612
|
+
wouldChangeItems.push({
|
|
62613
|
+
path: change.path,
|
|
62614
|
+
currentOwner: "(app policy owner)"
|
|
62615
|
+
});
|
|
62616
|
+
}
|
|
62617
|
+
continue;
|
|
62618
|
+
}
|
|
62619
|
+
if (change.name !== "edited")
|
|
62620
|
+
continue;
|
|
62621
|
+
const beforeContent = change.before;
|
|
62622
|
+
if (!beforeContent)
|
|
62623
|
+
continue;
|
|
62624
|
+
let currentOwner;
|
|
62625
|
+
if (typeStr === "script") {
|
|
62626
|
+
if (change.path.endsWith(".script.yaml") || change.path.endsWith(".script.json")) {
|
|
62627
|
+
const hasOboMatch = beforeContent.match(/has_on_behalf_of:\s*(true)/);
|
|
62628
|
+
if (hasOboMatch) {
|
|
62629
|
+
currentOwner = "(script owner)";
|
|
62630
|
+
} else {
|
|
62631
|
+
const emailMatch = beforeContent.match(/on_behalf_of_email:\s*["']?([^\s"']+)["']?/);
|
|
62632
|
+
if (emailMatch) {
|
|
62633
|
+
currentOwner = emailMatch[1];
|
|
62634
|
+
}
|
|
62635
|
+
}
|
|
62636
|
+
}
|
|
62637
|
+
} else if (typeStr === "flow") {
|
|
62638
|
+
if (change.path.endsWith("flow.yaml") || change.path.endsWith("flow.json")) {
|
|
62639
|
+
const hasOboMatch = beforeContent.match(/has_on_behalf_of:\s*(true)/);
|
|
62640
|
+
if (hasOboMatch) {
|
|
62641
|
+
wouldChangeItems.push({
|
|
62642
|
+
path: change.path,
|
|
62643
|
+
currentOwner: "(flow owner)"
|
|
62644
|
+
});
|
|
62645
|
+
}
|
|
62646
|
+
}
|
|
62647
|
+
continue;
|
|
62648
|
+
} else if (typeStr === "app") {
|
|
62649
|
+
wouldChangeItems.push({
|
|
62650
|
+
path: change.path,
|
|
62651
|
+
currentOwner: "(app policy owner)"
|
|
62652
|
+
});
|
|
62653
|
+
continue;
|
|
62654
|
+
} else if (typeStr === "schedule") {
|
|
62655
|
+
const match2 = beforeContent.match(/email:\s*["']?([^\s"']+)["']?/);
|
|
62656
|
+
if (match2) {
|
|
62657
|
+
currentOwner = match2[1];
|
|
62658
|
+
}
|
|
62659
|
+
} else if (typeStr.endsWith("_trigger")) {
|
|
62660
|
+
wouldChangeItems.push({
|
|
62661
|
+
path: change.path,
|
|
62662
|
+
currentOwner: "(trigger owner)"
|
|
62663
|
+
});
|
|
62664
|
+
continue;
|
|
62665
|
+
}
|
|
62666
|
+
if (currentOwner && currentOwner !== userEmail) {
|
|
62667
|
+
wouldChangeItems.push({ path: change.path, currentOwner });
|
|
62668
|
+
}
|
|
62669
|
+
}
|
|
62670
|
+
if (wouldChangeItems.length === 0)
|
|
62671
|
+
return;
|
|
62672
|
+
const itemList = wouldChangeItems.map((item) => ` - ${item.path} (current owner: ${item.currentOwner})`).join(`
|
|
62673
|
+
`);
|
|
62674
|
+
const message = `You are not an admin or member of 'wm_deployers'. The following ${wouldChangeItems.length} item(s) ` + `will have their permissioned_as/email changed to your user (${userEmail}):
|
|
62675
|
+
${itemList}`;
|
|
62676
|
+
if (acceptOverride) {
|
|
62677
|
+
warn(colors.yellow(`Warning: ${message}`));
|
|
62678
|
+
return;
|
|
62679
|
+
}
|
|
62680
|
+
if (isInteractive) {
|
|
62681
|
+
warn(colors.yellow(message));
|
|
62682
|
+
const proceed = await Confirm.prompt({
|
|
62683
|
+
message: "Do you want to proceed? (use --accept-overriding-permissioned-as-with-self to skip this prompt)",
|
|
62684
|
+
default: false
|
|
62685
|
+
});
|
|
62686
|
+
if (!proceed) {
|
|
62687
|
+
info("Push cancelled.");
|
|
62688
|
+
process.exit(0);
|
|
62689
|
+
}
|
|
62690
|
+
} else {
|
|
62691
|
+
error(colors.red(`${message}
|
|
62692
|
+
|
|
62693
|
+
Use --accept-overriding-permissioned-as-with-self to proceed anyway.`));
|
|
62694
|
+
process.exit(1);
|
|
62695
|
+
}
|
|
62696
|
+
}
|
|
62697
|
+
var init_permissioned_as = __esm(async () => {
|
|
62698
|
+
init_services_gen();
|
|
62699
|
+
init_log();
|
|
62700
|
+
init_colors2();
|
|
62701
|
+
await __promiseAll([
|
|
62702
|
+
init_confirm(),
|
|
62703
|
+
init_types()
|
|
62704
|
+
]);
|
|
62705
|
+
});
|
|
62706
|
+
|
|
62423
62707
|
// src/commands/resource/resource.ts
|
|
62424
62708
|
import { mkdir as mkdir4, stat as stat6, writeFile as writeFile6, readdir as readdir3, readFile as readFile6 } from "node:fs/promises";
|
|
62425
62709
|
import nodePath from "node:path";
|
|
@@ -63652,7 +63936,7 @@ async function findFilesetResourceFile(changePath) {
|
|
|
63652
63936
|
}
|
|
63653
63937
|
throw new Error(`No resource metadata file found for fileset resource: ${changePath}`);
|
|
63654
63938
|
}
|
|
63655
|
-
function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, resourceTypeToIsFileset, ignoreCodebaseChanges) {
|
|
63939
|
+
function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, resourceTypeToIsFileset, ignoreCodebaseChanges, stripOnBehalfOf) {
|
|
63656
63940
|
let _moduleScriptPaths = null;
|
|
63657
63941
|
async function getModuleScriptPaths() {
|
|
63658
63942
|
if (_moduleScriptPaths === null) {
|
|
@@ -63739,6 +64023,10 @@ function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, re
|
|
|
63739
64023
|
}
|
|
63740
64024
|
};
|
|
63741
64025
|
}
|
|
64026
|
+
if (stripOnBehalfOf) {
|
|
64027
|
+
flow.has_on_behalf_of = !!flow.on_behalf_of_email;
|
|
64028
|
+
delete flow.on_behalf_of_email;
|
|
64029
|
+
}
|
|
63742
64030
|
yield {
|
|
63743
64031
|
isDirectory: false,
|
|
63744
64032
|
path: path9.join(finalPath, "flow.yaml"),
|
|
@@ -63942,6 +64230,10 @@ function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, re
|
|
|
63942
64230
|
if (ignoreCodebaseChanges && parsed["codebase"]) {
|
|
63943
64231
|
parsed["codebase"] = undefined;
|
|
63944
64232
|
}
|
|
64233
|
+
if (stripOnBehalfOf) {
|
|
64234
|
+
parsed["has_on_behalf_of"] = !!parsed["on_behalf_of_email"];
|
|
64235
|
+
delete parsed["on_behalf_of_email"];
|
|
64236
|
+
}
|
|
63945
64237
|
delete parsed["modules"];
|
|
63946
64238
|
return useYaml ? import_yaml11.stringify(parsed, yamlOptions) : JSON.stringify(parsed, null, 2);
|
|
63947
64239
|
}
|
|
@@ -63964,14 +64256,30 @@ function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, re
|
|
|
63964
64256
|
}
|
|
63965
64257
|
return useYaml ? import_yaml11.stringify(parsed, yamlOptions) : JSON.stringify(parsed, null, 2);
|
|
63966
64258
|
}
|
|
63967
|
-
|
|
64259
|
+
if (isJson && kind != "dependencies") {
|
|
63968
64260
|
try {
|
|
63969
|
-
|
|
64261
|
+
const parsed = JSON.parse(content);
|
|
64262
|
+
if (stripOnBehalfOf) {
|
|
64263
|
+
const isSchedule = p.endsWith(".schedule.json");
|
|
64264
|
+
const isTrigger = p.endsWith("_trigger.json");
|
|
64265
|
+
if (isSchedule) {
|
|
64266
|
+
parsed["has_permissioned_as"] = !!parsed["permissioned_as"];
|
|
64267
|
+
delete parsed["permissioned_as"];
|
|
64268
|
+
delete parsed["email"];
|
|
64269
|
+
delete parsed["edited_by"];
|
|
64270
|
+
} else if (isTrigger) {
|
|
64271
|
+
parsed["has_permissioned_as"] = !!parsed["permissioned_as"];
|
|
64272
|
+
delete parsed["permissioned_as"];
|
|
64273
|
+
delete parsed["edited_by"];
|
|
64274
|
+
}
|
|
64275
|
+
}
|
|
64276
|
+
return useYaml ? import_yaml11.stringify(parsed, yamlOptions) : JSON.stringify(parsed, null, 2);
|
|
63970
64277
|
} catch (error2) {
|
|
63971
64278
|
error(`Failed to parse JSON content at path: ${p}`);
|
|
63972
64279
|
throw error2;
|
|
63973
64280
|
}
|
|
63974
|
-
}
|
|
64281
|
+
}
|
|
64282
|
+
return content;
|
|
63975
64283
|
}
|
|
63976
64284
|
}
|
|
63977
64285
|
];
|
|
@@ -64692,7 +65000,7 @@ async function pull(opts) {
|
|
|
64692
65000
|
resourceTypeToIsFileset = parsed.filesetMap;
|
|
64693
65001
|
} catch {}
|
|
64694
65002
|
const zipFile = await downloadZip(workspace, opts.plainSecrets, opts.skipVariables, opts.skipResources, opts.skipResourceTypes, opts.skipSecrets, opts.includeSchedules, opts.includeTriggers, opts.includeUsers, opts.includeGroups, opts.includeSettings, opts.includeKey, opts.skipWorkspaceDependencies, opts.defaultTs);
|
|
64695
|
-
const remote = ZipFSElement(zipFile, !opts.json, opts.defaultTs ?? "bun", resourceTypeToFormatExtension, resourceTypeToIsFileset, true);
|
|
65003
|
+
const remote = ZipFSElement(zipFile, !opts.json, opts.defaultTs ?? "bun", resourceTypeToFormatExtension, resourceTypeToIsFileset, true, parseSyncBehavior(opts.syncBehavior) >= 1);
|
|
64696
65004
|
const local = !opts.stateful ? await FSFSElement(process.cwd(), codebases, true) : await FSFSElement(path9.join(process.cwd(), ".wmill"), [], true);
|
|
64697
65005
|
const changes = await compareDynFSElement(remote, local, await ignoreF(opts), opts.json ?? false, opts, false, codebases, true, specificItems, wsNameForFiles, true);
|
|
64698
65006
|
info(`remote (${workspace.name}) -> local: ${changes.length} changes to apply`);
|
|
@@ -64859,7 +65167,7 @@ Done! All ${changes.length} changes applied locally and wmill-lock.yaml updated.
|
|
|
64859
65167
|
console.log(JSON.stringify({ success: true, message: "No changes to apply", total: 0 }, null, 2));
|
|
64860
65168
|
}
|
|
64861
65169
|
}
|
|
64862
|
-
function prettyChanges(changes, specificItems, branchOverride) {
|
|
65170
|
+
function prettyChanges(changes, specificItems, branchOverride, folderDefaultAnnotations) {
|
|
64863
65171
|
for (const change of changes) {
|
|
64864
65172
|
let displayPath = change.path;
|
|
64865
65173
|
let wsNote = "";
|
|
@@ -64870,8 +65178,10 @@ function prettyChanges(changes, specificItems, branchOverride) {
|
|
|
64870
65178
|
wsNote = " (workspace-specific)";
|
|
64871
65179
|
}
|
|
64872
65180
|
}
|
|
65181
|
+
const folderNote = folderDefaultAnnotations?.get(change.path);
|
|
65182
|
+
const extraNote = folderNote ? colors.cyan(` (will be permissioned as ${folderNote} via folder default)`) : "";
|
|
64873
65183
|
if (change.name === "added") {
|
|
64874
|
-
info(colors.green(`+ ${getTypeStrFromPath(change.path)} ` + displayPath + colors.gray(wsNote)));
|
|
65184
|
+
info(colors.green(`+ ${getTypeStrFromPath(change.path)} ` + displayPath + colors.gray(wsNote)) + extraNote);
|
|
64875
65185
|
} else if (change.name === "deleted") {
|
|
64876
65186
|
info(colors.red(`- ${getTypeStrFromPath(change.path)} ` + displayPath + colors.gray(wsNote)));
|
|
64877
65187
|
} else if (change.name === "edited") {
|
|
@@ -64979,7 +65289,7 @@ Push aborted: ${lockIssues.length} script(s) missing locks.`));
|
|
|
64979
65289
|
resourceTypeToFormatExtension = parsed.formatExtMap;
|
|
64980
65290
|
resourceTypeToIsFileset = parsed.filesetMap;
|
|
64981
65291
|
} catch {}
|
|
64982
|
-
const remote = ZipFSElement(await downloadZip(workspace, opts.plainSecrets, opts.skipVariables, opts.skipResources, opts.skipResourceTypes, opts.skipSecrets, opts.includeSchedules, opts.includeTriggers, opts.includeUsers, opts.includeGroups, opts.includeSettings, opts.includeKey, opts.skipWorkspaceDependencies, opts.defaultTs), !opts.json, opts.defaultTs ?? "bun", resourceTypeToFormatExtension, resourceTypeToIsFileset, false);
|
|
65292
|
+
const remote = ZipFSElement(await downloadZip(workspace, opts.plainSecrets, opts.skipVariables, opts.skipResources, opts.skipResourceTypes, opts.skipSecrets, opts.includeSchedules, opts.includeTriggers, opts.includeUsers, opts.includeGroups, opts.includeSettings, opts.includeKey, opts.skipWorkspaceDependencies, opts.defaultTs), !opts.json, opts.defaultTs ?? "bun", resourceTypeToFormatExtension, resourceTypeToIsFileset, false, parseSyncBehavior(opts.syncBehavior) >= 1);
|
|
64983
65293
|
const local = await FSFSElement(path9.join(process.cwd(), ""), codebases, false);
|
|
64984
65294
|
const changes = await compareDynFSElement(local, remote, await ignoreF(opts), opts.json ?? false, opts, true, codebases, false, specificItems, wsNameForFiles, false);
|
|
64985
65295
|
const rawWorkspaceDependencies = await getRawWorkspaceDependencies(true);
|
|
@@ -65152,13 +65462,59 @@ ${folderList}
|
|
|
65152
65462
|
return;
|
|
65153
65463
|
}
|
|
65154
65464
|
if (changes.length > 0) {
|
|
65465
|
+
let folderDefaultAnnotations;
|
|
65466
|
+
if (parseSyncBehavior(opts.syncBehavior) >= 1) {
|
|
65467
|
+
folderDefaultAnnotations = new Map;
|
|
65468
|
+
const folderRulesCache = new Map;
|
|
65469
|
+
for (const change of changes) {
|
|
65470
|
+
if (change.name !== "added")
|
|
65471
|
+
continue;
|
|
65472
|
+
const match2 = change.path.match(/^f\/([^/]+)\//);
|
|
65473
|
+
if (!match2)
|
|
65474
|
+
continue;
|
|
65475
|
+
const folderName2 = match2[1];
|
|
65476
|
+
if (!folderRulesCache.has(folderName2)) {
|
|
65477
|
+
try {
|
|
65478
|
+
const folder = await getFolder({ workspace: workspace.workspaceId, name: folderName2 });
|
|
65479
|
+
folderRulesCache.set(folderName2, folder.default_permissioned_as ?? []);
|
|
65480
|
+
} catch {
|
|
65481
|
+
folderRulesCache.set(folderName2, []);
|
|
65482
|
+
}
|
|
65483
|
+
}
|
|
65484
|
+
const rules = folderRulesCache.get(folderName2);
|
|
65485
|
+
const remotePath = change.path.replace(/\.(script|schedule|http_trigger|websocket_trigger|kafka_trigger|nats_trigger|postgres_trigger|mqtt_trigger|sqs_trigger|gcp_trigger|email_trigger)\.(yaml|json)$/, "").replace(/(\.flow|__flow)\/flow\.(yaml|json)$/, "").replace(/\.(app|raw_app)(\/app\.(yaml|json))?$/, "");
|
|
65486
|
+
const relative6 = remotePath.slice(`f/${folderName2}/`.length);
|
|
65487
|
+
if (!relative6)
|
|
65488
|
+
continue;
|
|
65489
|
+
for (const rule of rules) {
|
|
65490
|
+
if (minimatch(relative6, rule.path_glob)) {
|
|
65491
|
+
folderDefaultAnnotations.set(change.path, rule.permissioned_as);
|
|
65492
|
+
break;
|
|
65493
|
+
}
|
|
65494
|
+
}
|
|
65495
|
+
}
|
|
65496
|
+
}
|
|
65155
65497
|
if (!opts.jsonOutput) {
|
|
65156
|
-
prettyChanges(changes, specificItems, wsNameForFiles);
|
|
65498
|
+
prettyChanges(changes, specificItems, wsNameForFiles, folderDefaultAnnotations);
|
|
65157
65499
|
}
|
|
65158
65500
|
if (opts.dryRun) {
|
|
65159
65501
|
info(colors.gray(`Dry run complete.`));
|
|
65160
65502
|
return;
|
|
65161
65503
|
}
|
|
65504
|
+
let permissionedAsContext = undefined;
|
|
65505
|
+
if (parseSyncBehavior(opts.syncBehavior) >= 1) {
|
|
65506
|
+
const user = await whoami({ workspace: workspace.workspaceId });
|
|
65507
|
+
const userIsAdminOrDeployer = user.is_admin || (user.groups ?? []).includes("wm_deployers");
|
|
65508
|
+
debug(`permissioned_as: user=${user.email}, is_admin=${user.is_admin}, groups=${JSON.stringify(user.groups)}, isAdminOrDeployer=${userIsAdminOrDeployer}`);
|
|
65509
|
+
permissionedAsContext = {
|
|
65510
|
+
userCache: new Map,
|
|
65511
|
+
userIsAdminOrDeployer,
|
|
65512
|
+
userEmail: user.email
|
|
65513
|
+
};
|
|
65514
|
+
await preCheckPermissionedAs(changes, user.email, userIsAdminOrDeployer, opts.acceptOverridingPermissionedAsWithSelf ?? false, !!process.stdin.isTTY);
|
|
65515
|
+
} else if (folderDefaultAnnotations && folderDefaultAnnotations.size > 0) {
|
|
65516
|
+
warn(colors.yellow(`This workspace has folder default_permissioned_as rules that affect ${folderDefaultAnnotations.size} item(s) being pushed, ` + `but syncBehavior is not set in wmill.yaml. Add 'syncBehavior: v1' to enable ownership preservation on update and on_behalf_of stripping on pull.`));
|
|
65517
|
+
}
|
|
65162
65518
|
if (!opts.yes && !await Confirm.prompt({
|
|
65163
65519
|
message: `Do you want to apply these ${changes.length} changes to the remote?`,
|
|
65164
65520
|
default: true
|
|
@@ -65187,17 +65543,24 @@ ${folderList}
|
|
|
65187
65543
|
if (parallelizationFactor <= 0) {
|
|
65188
65544
|
parallelizationFactor = 1;
|
|
65189
65545
|
}
|
|
65190
|
-
const
|
|
65191
|
-
|
|
65546
|
+
const allGrouped = Array.from(groupedChanges.entries());
|
|
65547
|
+
const isFolderMetaGroup = ([basePath]) => basePath.endsWith(`${SEP8}folder`) || basePath === "folder";
|
|
65548
|
+
const folderMetaGroups = allGrouped.filter(isFolderMetaGroup);
|
|
65549
|
+
const groupedChangesArray = allGrouped.filter((g) => !isFolderMetaGroup(g));
|
|
65550
|
+
info(`found changes for ${allGrouped.length} items with a total of ${allGrouped.reduce((acc, [_, changes2]) => acc + changes2.length, 0)} files to process`);
|
|
65192
65551
|
if (parallelizationFactor > 1) {
|
|
65193
65552
|
info(`Parallelizing ${parallelizationFactor} changes at a time`);
|
|
65194
65553
|
}
|
|
65195
65554
|
const pool = new Set;
|
|
65196
|
-
const queue = [...groupedChangesArray];
|
|
65555
|
+
const queue = [...folderMetaGroups, ...groupedChangesArray];
|
|
65556
|
+
let folderPhaseRemaining = folderMetaGroups.length;
|
|
65557
|
+
const effectiveParallelism = () => folderPhaseRemaining > 0 ? 1 : parallelizationFactor;
|
|
65197
65558
|
const cachedWsNameForPush = wsNameForFiles || (isGitRepository() ? getCurrentGitBranch() : null);
|
|
65198
65559
|
while (queue.length > 0 || pool.size > 0) {
|
|
65199
|
-
while (pool.size <
|
|
65200
|
-
|
|
65560
|
+
while (pool.size < effectiveParallelism() && queue.length > 0) {
|
|
65561
|
+
const [groupBasePath, initialChanges] = queue.shift();
|
|
65562
|
+
let changes2 = initialChanges;
|
|
65563
|
+
const isFolderGroup = groupBasePath.endsWith(`${SEP8}folder`) || groupBasePath === "folder";
|
|
65201
65564
|
const promise = (async () => {
|
|
65202
65565
|
const alreadySynced = [];
|
|
65203
65566
|
const deletedVarsResPaths = [];
|
|
@@ -65221,12 +65584,12 @@ ${folderList}
|
|
|
65221
65584
|
}
|
|
65222
65585
|
}
|
|
65223
65586
|
if (change.name === "edited") {
|
|
65224
|
-
if (await handleScriptMetadata(change.path, workspace, alreadySynced, opts.message, rawWorkspaceDependencies, codebases, opts)) {
|
|
65587
|
+
if (await handleScriptMetadata(change.path, workspace, alreadySynced, opts.message, rawWorkspaceDependencies, codebases, opts, permissionedAsContext)) {
|
|
65225
65588
|
if (stateTarget) {
|
|
65226
65589
|
await writeFile7(stateTarget, change.after, "utf-8");
|
|
65227
65590
|
}
|
|
65228
65591
|
continue;
|
|
65229
|
-
} else if (await handleFile(change.path, workspace, alreadySynced, opts.message, opts, rawWorkspaceDependencies, codebases)) {
|
|
65592
|
+
} else if (await handleFile(change.path, workspace, alreadySynced, opts.message, opts, rawWorkspaceDependencies, codebases, permissionedAsContext)) {
|
|
65230
65593
|
if (stateTarget) {
|
|
65231
65594
|
await writeFile7(stateTarget, change.after, "utf-8");
|
|
65232
65595
|
}
|
|
@@ -65282,14 +65645,14 @@ ${folderList}
|
|
|
65282
65645
|
if (specificItems && isSpecificItem(change.path, specificItems)) {
|
|
65283
65646
|
originalWorkspaceSpecificPath = getWorkspaceSpecificPath(change.path, specificItems, wsNameForFiles);
|
|
65284
65647
|
}
|
|
65285
|
-
await pushObj(workspace.workspaceId, change.path, oldObj, newObj, opts.plainSecrets ?? false, alreadySynced, opts.message, originalWorkspaceSpecificPath);
|
|
65648
|
+
await pushObj(workspace.workspaceId, change.path, oldObj, newObj, opts.plainSecrets ?? false, alreadySynced, opts.message, originalWorkspaceSpecificPath, permissionedAsContext);
|
|
65286
65649
|
if (stateTarget) {
|
|
65287
65650
|
await writeFile7(stateTarget, change.after, "utf-8");
|
|
65288
65651
|
}
|
|
65289
65652
|
} else if (change.name === "added") {
|
|
65290
65653
|
if (change.path.endsWith(".script.json") || change.path.endsWith(".script.yaml") || change.path.endsWith(".lock") || isFileResource(change.path) || isFilesetResource(change.path)) {
|
|
65291
65654
|
continue;
|
|
65292
|
-
} else if (await handleFile(change.path, workspace, alreadySynced, opts.message, opts, rawWorkspaceDependencies, codebases)) {
|
|
65655
|
+
} else if (await handleFile(change.path, workspace, alreadySynced, opts.message, opts, rawWorkspaceDependencies, codebases, permissionedAsContext)) {
|
|
65293
65656
|
continue;
|
|
65294
65657
|
} else if (isScriptModulePath(change.path)) {
|
|
65295
65658
|
await pushParentScriptForModule(change.path, workspace, alreadySynced, opts.message, opts, rawWorkspaceDependencies, codebases);
|
|
@@ -65307,7 +65670,7 @@ ${folderList}
|
|
|
65307
65670
|
localFilePath = workspaceSpecificPath;
|
|
65308
65671
|
}
|
|
65309
65672
|
}
|
|
65310
|
-
await pushObj(workspace.workspaceId, change.path, undefined, obj, opts.plainSecrets ?? false, [], opts.message, localFilePath);
|
|
65673
|
+
await pushObj(workspace.workspaceId, change.path, undefined, obj, opts.plainSecrets ?? false, [], opts.message, localFilePath, permissionedAsContext);
|
|
65311
65674
|
if (stateTarget) {
|
|
65312
65675
|
await writeFile7(stateTarget, change.content, "utf-8");
|
|
65313
65676
|
}
|
|
@@ -65583,7 +65946,11 @@ ${folderList}
|
|
|
65583
65946
|
}
|
|
65584
65947
|
})();
|
|
65585
65948
|
pool.add(promise);
|
|
65586
|
-
promise.then(() =>
|
|
65949
|
+
promise.then(() => {
|
|
65950
|
+
pool.delete(promise);
|
|
65951
|
+
if (isFolderGroup)
|
|
65952
|
+
folderPhaseRemaining--;
|
|
65953
|
+
});
|
|
65587
65954
|
}
|
|
65588
65955
|
if (pool.size > 0) {
|
|
65589
65956
|
await Promise.race(pool);
|
|
@@ -65659,6 +66026,7 @@ var init_sync = __esm(async () => {
|
|
|
65659
66026
|
init_script(),
|
|
65660
66027
|
init_utils(),
|
|
65661
66028
|
init_conf(),
|
|
66029
|
+
init_permissioned_as(),
|
|
65662
66030
|
init_specific_items(),
|
|
65663
66031
|
init_types(),
|
|
65664
66032
|
init_codebase(),
|
|
@@ -65676,7 +66044,7 @@ var init_sync = __esm(async () => {
|
|
|
65676
66044
|
aliasDuplicateObjects: false,
|
|
65677
66045
|
singleQuote: true
|
|
65678
66046
|
};
|
|
65679
|
-
command7 = new Command().description("sync local with a remote workspaces or the opposite (push or pull)").action(() => info("2 actions available, pull and push. Use -h to display help.")).command("pull").description("Pull any remote changes and apply them locally.").option("--yes", "Pull without needing confirmation").option("--dry-run", "Show changes that would be pulled without actually pushing").option("--plain-secrets", "Pull secrets as plain text").option("--json", "Use JSON instead of YAML").option("--skip-variables", "Skip syncing variables (including secrets)").option("--skip-secrets", "Skip syncing only secrets variables").option("--include-secrets", "Include secrets in sync (overrides skipSecrets in wmill.yaml)").option("--skip-resources", "Skip syncing resources").option("--skip-resource-types", "Skip syncing resource types").option("--skip-scripts", "Skip syncing scripts").option("--skip-flows", "Skip syncing flows").option("--skip-apps", "Skip syncing apps").option("--skip-folders", "Skip syncing folders").option("--skip-workspace-dependencies", "Skip syncing workspace dependencies").option("--include-schedules", "Include syncing schedules").option("--include-triggers", "Include syncing triggers").option("--include-users", "Include syncing users").option("--include-groups", "Include syncing groups").option("--include-settings", "Include syncing workspace settings").option("--include-key", "Include workspace encryption key").option("--skip-branch-validation", "Skip git branch validation and prompts").option("--json-output", "Output results in JSON format").option("-i --includes <patterns:file[]>", "Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string). Overrides wmill.yaml includes").option("-e --excludes <patterns:file[]>", "Comma separated patterns to specify which file to NOT take into account. Overrides wmill.yaml excludes").option("--extra-includes <patterns:file[]>", "Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string). Useful to still take wmill.yaml into account and act as a second pattern to satisfy").option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo) when multiple repositories exist").option("--promotion <branch:string>", "Use promotionOverrides from the specified branch instead of regular overrides").option("--branch, --env <branch:string>", "[Deprecated: use --workspace] Override the current git branch/environment").action(pull).command("push").description("Push any local changes and apply them remotely.").option("--yes", "Push without needing confirmation").option("--dry-run", "Show changes that would be pushed without actually pushing").option("--plain-secrets", "Push secrets as plain text").option("--json", "Use JSON instead of YAML").option("--skip-variables", "Skip syncing variables (including secrets)").option("--skip-secrets", "Skip syncing only secrets variables").option("--include-secrets", "Include secrets in sync (overrides skipSecrets in wmill.yaml)").option("--skip-resources", "Skip syncing resources").option("--skip-resource-types", "Skip syncing resource types").option("--skip-scripts", "Skip syncing scripts").option("--skip-flows", "Skip syncing flows").option("--skip-apps", "Skip syncing apps").option("--skip-folders", "Skip syncing folders").option("--skip-workspace-dependencies", "Skip syncing workspace dependencies").option("--include-schedules", "Include syncing schedules").option("--include-triggers", "Include syncing triggers").option("--include-users", "Include syncing users").option("--include-groups", "Include syncing groups").option("--include-settings", "Include syncing workspace settings").option("--include-key", "Include workspace encryption key").option("--skip-branch-validation", "Skip git branch validation and prompts").option("--json-output", "Output results in JSON format").option("-i --includes <patterns:file[]>", "Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string)").option("-e --excludes <patterns:file[]>", "Comma separated patterns to specify which file to NOT take into account.").option("--extra-includes <patterns:file[]>", "Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string). Useful to still take wmill.yaml into account and act as a second pattern to satisfy").option("--message <message:string>", "Include a message that will be added to all scripts/flows/apps updated during this push").option("--parallel <number>", "Number of changes to process in parallel").option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo) when multiple repositories exist").option("--branch, --env <branch:string>", "[Deprecated: use --workspace] Override the current git branch/environment").option("--lint", "Run lint validation before pushing").option("--locks-required", "Fail if scripts or flow inline scripts that need locks have no locks").option("--auto-metadata", "Automatically regenerate stale metadata (locks and schemas) before pushing").action(push4);
|
|
66047
|
+
command7 = new Command().description("sync local with a remote workspaces or the opposite (push or pull)").action(() => info("2 actions available, pull and push. Use -h to display help.")).command("pull").description("Pull any remote changes and apply them locally.").option("--yes", "Pull without needing confirmation").option("--dry-run", "Show changes that would be pulled without actually pushing").option("--plain-secrets", "Pull secrets as plain text").option("--json", "Use JSON instead of YAML").option("--skip-variables", "Skip syncing variables (including secrets)").option("--skip-secrets", "Skip syncing only secrets variables").option("--include-secrets", "Include secrets in sync (overrides skipSecrets in wmill.yaml)").option("--skip-resources", "Skip syncing resources").option("--skip-resource-types", "Skip syncing resource types").option("--skip-scripts", "Skip syncing scripts").option("--skip-flows", "Skip syncing flows").option("--skip-apps", "Skip syncing apps").option("--skip-folders", "Skip syncing folders").option("--skip-workspace-dependencies", "Skip syncing workspace dependencies").option("--include-schedules", "Include syncing schedules").option("--include-triggers", "Include syncing triggers").option("--include-users", "Include syncing users").option("--include-groups", "Include syncing groups").option("--include-settings", "Include syncing workspace settings").option("--include-key", "Include workspace encryption key").option("--skip-branch-validation", "Skip git branch validation and prompts").option("--json-output", "Output results in JSON format").option("-i --includes <patterns:file[]>", "Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string). Overrides wmill.yaml includes").option("-e --excludes <patterns:file[]>", "Comma separated patterns to specify which file to NOT take into account. Overrides wmill.yaml excludes").option("--extra-includes <patterns:file[]>", "Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string). Useful to still take wmill.yaml into account and act as a second pattern to satisfy").option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo) when multiple repositories exist").option("--promotion <branch:string>", "Use promotionOverrides from the specified branch instead of regular overrides").option("--branch, --env <branch:string>", "[Deprecated: use --workspace] Override the current git branch/environment").action(pull).command("push").description("Push any local changes and apply them remotely.").option("--yes", "Push without needing confirmation").option("--dry-run", "Show changes that would be pushed without actually pushing").option("--plain-secrets", "Push secrets as plain text").option("--json", "Use JSON instead of YAML").option("--skip-variables", "Skip syncing variables (including secrets)").option("--skip-secrets", "Skip syncing only secrets variables").option("--include-secrets", "Include secrets in sync (overrides skipSecrets in wmill.yaml)").option("--skip-resources", "Skip syncing resources").option("--skip-resource-types", "Skip syncing resource types").option("--skip-scripts", "Skip syncing scripts").option("--skip-flows", "Skip syncing flows").option("--skip-apps", "Skip syncing apps").option("--skip-folders", "Skip syncing folders").option("--skip-workspace-dependencies", "Skip syncing workspace dependencies").option("--include-schedules", "Include syncing schedules").option("--include-triggers", "Include syncing triggers").option("--include-users", "Include syncing users").option("--include-groups", "Include syncing groups").option("--include-settings", "Include syncing workspace settings").option("--include-key", "Include workspace encryption key").option("--skip-branch-validation", "Skip git branch validation and prompts").option("--json-output", "Output results in JSON format").option("-i --includes <patterns:file[]>", "Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string)").option("-e --excludes <patterns:file[]>", "Comma separated patterns to specify which file to NOT take into account.").option("--extra-includes <patterns:file[]>", "Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string). Useful to still take wmill.yaml into account and act as a second pattern to satisfy").option("--message <message:string>", "Include a message that will be added to all scripts/flows/apps updated during this push").option("--parallel <number>", "Number of changes to process in parallel").option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo) when multiple repositories exist").option("--branch, --env <branch:string>", "[Deprecated: use --workspace] Override the current git branch/environment").option("--lint", "Run lint validation before pushing").option("--locks-required", "Fail if scripts or flow inline scripts that need locks have no locks").option("--auto-metadata", "Automatically regenerate stale metadata (locks and schemas) before pushing").option("--accept-overriding-permissioned-as-with-self", "Accept that items with a different permissioned_as will be updated with your own user").action(push4);
|
|
65680
66048
|
sync_default = command7;
|
|
65681
66049
|
});
|
|
65682
66050
|
|
|
@@ -69487,7 +69855,7 @@ function replaceInlineScripts2(rec, localPath, addType) {
|
|
|
69487
69855
|
function isExecutionModeAnonymous(app) {
|
|
69488
69856
|
return app?.["policy"]?.["execution_mode"] == "anonymous";
|
|
69489
69857
|
}
|
|
69490
|
-
async function pushApp(workspace, remotePath, localPath, message) {
|
|
69858
|
+
async function pushApp(workspace, remotePath, localPath, message, permissionedAsContext) {
|
|
69491
69859
|
if (alreadySynced2.includes(localPath)) {
|
|
69492
69860
|
return;
|
|
69493
69861
|
}
|
|
@@ -69500,6 +69868,12 @@ async function pushApp(workspace, remotePath, localPath, message) {
|
|
|
69500
69868
|
path: remotePath
|
|
69501
69869
|
});
|
|
69502
69870
|
} catch {}
|
|
69871
|
+
let remoteOnBehalfOf;
|
|
69872
|
+
let remoteOnBehalfOfEmail;
|
|
69873
|
+
if (app?.policy) {
|
|
69874
|
+
remoteOnBehalfOf = app.policy.on_behalf_of;
|
|
69875
|
+
remoteOnBehalfOfEmail = app.policy.on_behalf_of_email;
|
|
69876
|
+
}
|
|
69503
69877
|
if (isExecutionModeAnonymous(app)) {
|
|
69504
69878
|
app.public = true;
|
|
69505
69879
|
}
|
|
@@ -69513,6 +69887,17 @@ async function pushApp(workspace, remotePath, localPath, message) {
|
|
|
69513
69887
|
const localApp = await yamlParseFile(path17);
|
|
69514
69888
|
replaceInlineScripts2(localApp.value, localPath, true);
|
|
69515
69889
|
await generatingPolicy2(localApp, remotePath, localApp?.["public"] ?? localApp?.["policy"]?.["execution_mode"] == "anonymous");
|
|
69890
|
+
const preserveFields = {};
|
|
69891
|
+
if (permissionedAsContext?.userIsAdminOrDeployer) {
|
|
69892
|
+
if (app) {
|
|
69893
|
+
if (localApp.policy && remoteOnBehalfOf) {
|
|
69894
|
+
localApp.policy.on_behalf_of = remoteOnBehalfOf;
|
|
69895
|
+
localApp.policy.on_behalf_of_email = remoteOnBehalfOfEmail;
|
|
69896
|
+
preserveFields.preserve_on_behalf_of = true;
|
|
69897
|
+
info(`Preserving ${remoteOnBehalfOfEmail ?? remoteOnBehalfOf} as permissioned_as for app ${remotePath}`);
|
|
69898
|
+
}
|
|
69899
|
+
}
|
|
69900
|
+
}
|
|
69516
69901
|
if (app) {
|
|
69517
69902
|
if (isSuperset(localApp, app)) {
|
|
69518
69903
|
info(colors.green(`App ${remotePath} is up to date`));
|
|
@@ -69524,7 +69909,8 @@ async function pushApp(workspace, remotePath, localPath, message) {
|
|
|
69524
69909
|
path: remotePath,
|
|
69525
69910
|
requestBody: {
|
|
69526
69911
|
deployment_message: message,
|
|
69527
|
-
...localApp
|
|
69912
|
+
...localApp,
|
|
69913
|
+
...preserveFields
|
|
69528
69914
|
}
|
|
69529
69915
|
});
|
|
69530
69916
|
} else {
|
|
@@ -69534,7 +69920,8 @@ async function pushApp(workspace, remotePath, localPath, message) {
|
|
|
69534
69920
|
requestBody: {
|
|
69535
69921
|
path: remotePath,
|
|
69536
69922
|
deployment_message: message,
|
|
69537
|
-
...localApp
|
|
69923
|
+
...localApp,
|
|
69924
|
+
...preserveFields
|
|
69538
69925
|
}
|
|
69539
69926
|
});
|
|
69540
69927
|
}
|
|
@@ -69639,6 +70026,31 @@ var init_app = __esm(async () => {
|
|
|
69639
70026
|
warn(colors.yellow('This command is deprecated. Use "wmill generate-metadata" instead.'));
|
|
69640
70027
|
const { generateLocksCommand: generateLocksCommand2 } = await init_app_metadata().then(() => exports_app_metadata);
|
|
69641
70028
|
await generateLocksCommand2(opts, appFolder);
|
|
70029
|
+
}).command("set-permissioned-as", "Set the on_behalf_of_email for an app (requires admin or wm_deployers group)").arguments("<path:string> <email:string>").action(async (opts, appPath, email) => {
|
|
70030
|
+
const workspace = await resolveWorkspace(opts);
|
|
70031
|
+
await requireLogin(opts);
|
|
70032
|
+
const { lookupUsernameByEmail: lookupUsernameByEmail2 } = await init_permissioned_as().then(() => exports_permissioned_as);
|
|
70033
|
+
const cache3 = new Map;
|
|
70034
|
+
const username = await lookupUsernameByEmail2(workspace.workspaceId, email, cache3);
|
|
70035
|
+
const remote = await getAppByPath({
|
|
70036
|
+
workspace: workspace.workspaceId,
|
|
70037
|
+
path: appPath
|
|
70038
|
+
});
|
|
70039
|
+
if (!remote)
|
|
70040
|
+
throw new Error(`App ${appPath} not found`);
|
|
70041
|
+
await updateApp({
|
|
70042
|
+
workspace: workspace.workspaceId,
|
|
70043
|
+
path: appPath,
|
|
70044
|
+
requestBody: {
|
|
70045
|
+
policy: {
|
|
70046
|
+
...remote.policy,
|
|
70047
|
+
on_behalf_of: `u/${username}`,
|
|
70048
|
+
on_behalf_of_email: email
|
|
70049
|
+
},
|
|
70050
|
+
preserve_on_behalf_of: true
|
|
70051
|
+
}
|
|
70052
|
+
});
|
|
70053
|
+
info(colors.green(`Updated permissioned_as for app ${appPath} to ${email}`));
|
|
69642
70054
|
});
|
|
69643
70055
|
app_default = command12;
|
|
69644
70056
|
});
|
|
@@ -69818,7 +70230,54 @@ var init_folder = __esm(async () => {
|
|
|
69818
70230
|
init_types()
|
|
69819
70231
|
]);
|
|
69820
70232
|
import_yaml24 = __toESM(require_dist(), 1);
|
|
69821
|
-
command13 = new Command().description("folder related commands").option("--json", "Output as JSON (for piping to jq)").action(list7).command("list", "list all folders").option("--json", "Output as JSON (for piping to jq)").action(list7).command("get", "get a folder's details").arguments("<name:string>").option("--json", "Output as JSON (for piping to jq)").action(get5).command("new", "create a new folder locally").arguments("<name:string>").option("--summary <summary:string>", "folder summary").action(newFolder).command("push", "push a local folder to the remote by name. This overrides any remote versions.").arguments("<name:string>").action(push6).command("add-missing", "create default folder.meta.yaml for all subdirectories of f/ that are missing one").option("-y, --yes", "skip confirmation prompt").action(addMissing)
|
|
70233
|
+
command13 = new Command().description("folder related commands").option("--json", "Output as JSON (for piping to jq)").action(list7).command("list", "list all folders").option("--json", "Output as JSON (for piping to jq)").action(list7).command("get", "get a folder's details").arguments("<name:string>").option("--json", "Output as JSON (for piping to jq)").action(get5).command("new", "create a new folder locally").arguments("<name:string>").option("--summary <summary:string>", "folder summary").action(newFolder).command("push", "push a local folder to the remote by name. This overrides any remote versions.").arguments("<name:string>").action(push6).command("add-missing", "create default folder.meta.yaml for all subdirectories of f/ that are missing one").option("-y, --yes", "skip confirmation prompt").action(addMissing).command("show-rules", "Show default_permissioned_as rules for a folder. Use --test-path to see which rule matches a given item path.").arguments("<name:string>").option("--test-path <path:string>", "Test which rule matches this item path (e.g. f/prod/jobs/my_script)").option("--json", "Output as JSON").action(async (opts, folderName2) => {
|
|
70234
|
+
const workspace = await resolveWorkspace(opts);
|
|
70235
|
+
await requireLogin(opts);
|
|
70236
|
+
const folder = await getFolder({
|
|
70237
|
+
workspace: workspace.workspaceId,
|
|
70238
|
+
name: folderName2
|
|
70239
|
+
});
|
|
70240
|
+
const rules = folder.default_permissioned_as ?? [];
|
|
70241
|
+
if (opts.json && !opts.testPath) {
|
|
70242
|
+
console.log(JSON.stringify(rules, null, 2));
|
|
70243
|
+
return;
|
|
70244
|
+
}
|
|
70245
|
+
if (rules.length === 0) {
|
|
70246
|
+
info(`Folder '${folderName2}' has no default_permissioned_as rules.`);
|
|
70247
|
+
return;
|
|
70248
|
+
}
|
|
70249
|
+
if (!opts.testPath) {
|
|
70250
|
+
info(colors.bold(`Rules for folder '${folderName2}' (first match wins):
|
|
70251
|
+
`));
|
|
70252
|
+
new Table2().header(["#", "path_glob", "permissioned_as"]).padding(2).border(true).body(rules.map((r, i) => [String(i + 1), r.path_glob, r.permissioned_as])).render();
|
|
70253
|
+
return;
|
|
70254
|
+
}
|
|
70255
|
+
const testPath = opts.testPath;
|
|
70256
|
+
const prefix = `f/${folderName2}/`;
|
|
70257
|
+
if (!testPath.startsWith(prefix)) {
|
|
70258
|
+
error(`Path '${testPath}' is not under folder '${folderName2}' (expected prefix '${prefix}')`);
|
|
70259
|
+
return;
|
|
70260
|
+
}
|
|
70261
|
+
const relative7 = testPath.slice(prefix.length);
|
|
70262
|
+
const { minimatch: minimatch2 } = await Promise.resolve().then(() => (init_esm2(), exports_esm));
|
|
70263
|
+
for (let i = 0;i < rules.length; i++) {
|
|
70264
|
+
const rule = rules[i];
|
|
70265
|
+
if (minimatch2(relative7, rule.path_glob)) {
|
|
70266
|
+
if (opts.json) {
|
|
70267
|
+
console.log(JSON.stringify({ matched: true, rule_index: i, rule, relative_path: relative7 }));
|
|
70268
|
+
} else {
|
|
70269
|
+
info(colors.green(`✓ Rule #${i + 1} matches: path_glob='${rule.path_glob}' → permissioned_as='${rule.permissioned_as}'`));
|
|
70270
|
+
info(colors.gray(` (relative path tested: '${relative7}')`));
|
|
70271
|
+
}
|
|
70272
|
+
return;
|
|
70273
|
+
}
|
|
70274
|
+
}
|
|
70275
|
+
if (opts.json) {
|
|
70276
|
+
console.log(JSON.stringify({ matched: false, relative_path: relative7 }));
|
|
70277
|
+
} else {
|
|
70278
|
+
info(colors.yellow(`No rule matches path '${testPath}' (relative: '${relative7}')`));
|
|
70279
|
+
}
|
|
70280
|
+
});
|
|
69822
70281
|
folder_default = command13;
|
|
69823
70282
|
});
|
|
69824
70283
|
|
|
@@ -70053,7 +70512,7 @@ async function get7(opts, path17) {
|
|
|
70053
70512
|
console.log(colors.bold("Enabled:") + " " + (s.enabled ? "true" : "false"));
|
|
70054
70513
|
}
|
|
70055
70514
|
}
|
|
70056
|
-
async function pushSchedule(workspace, path17, schedule, localSchedule) {
|
|
70515
|
+
async function pushSchedule(workspace, path17, schedule, localSchedule, permissionedAsContext) {
|
|
70057
70516
|
path17 = removeType(path17, "schedule").replaceAll(SEP16, "/");
|
|
70058
70517
|
debug(`Processing local schedule ${path17}`);
|
|
70059
70518
|
try {
|
|
@@ -70062,6 +70521,17 @@ async function pushSchedule(workspace, path17, schedule, localSchedule) {
|
|
|
70062
70521
|
} catch {
|
|
70063
70522
|
debug(`Schedule ${path17} does not exist on remote`);
|
|
70064
70523
|
}
|
|
70524
|
+
delete localSchedule.has_permissioned_as;
|
|
70525
|
+
const preserveFields = {};
|
|
70526
|
+
if (permissionedAsContext?.userIsAdminOrDeployer) {
|
|
70527
|
+
if (schedule) {
|
|
70528
|
+
preserveFields.preserve_permissioned_as = true;
|
|
70529
|
+
if (schedule.permissioned_as) {
|
|
70530
|
+
preserveFields.permissioned_as = schedule.permissioned_as;
|
|
70531
|
+
info(`Preserving ${schedule.permissioned_as} as permissioned_as for schedule ${path17}`);
|
|
70532
|
+
}
|
|
70533
|
+
}
|
|
70534
|
+
}
|
|
70065
70535
|
if (schedule) {
|
|
70066
70536
|
if (isSuperset(localSchedule, schedule)) {
|
|
70067
70537
|
debug(`Schedule ${path17} is up to date`);
|
|
@@ -70074,7 +70544,8 @@ async function pushSchedule(workspace, path17, schedule, localSchedule) {
|
|
|
70074
70544
|
workspace,
|
|
70075
70545
|
path: path17,
|
|
70076
70546
|
requestBody: {
|
|
70077
|
-
...localSchedule
|
|
70547
|
+
...localSchedule,
|
|
70548
|
+
...preserveFields
|
|
70078
70549
|
}
|
|
70079
70550
|
});
|
|
70080
70551
|
if (localSchedule.enabled != schedule.enabled) {
|
|
@@ -70098,7 +70569,8 @@ async function pushSchedule(workspace, path17, schedule, localSchedule) {
|
|
|
70098
70569
|
workspace,
|
|
70099
70570
|
requestBody: {
|
|
70100
70571
|
path: path17,
|
|
70101
|
-
...localSchedule
|
|
70572
|
+
...localSchedule,
|
|
70573
|
+
...preserveFields
|
|
70102
70574
|
}
|
|
70103
70575
|
});
|
|
70104
70576
|
} catch (e) {
|
|
@@ -70154,10 +70626,32 @@ var init_schedule = __esm(async () => {
|
|
|
70154
70626
|
init_auth(),
|
|
70155
70627
|
init_context(),
|
|
70156
70628
|
init_conf(),
|
|
70629
|
+
init_permissioned_as(),
|
|
70157
70630
|
init_types()
|
|
70158
70631
|
]);
|
|
70159
70632
|
import_yaml26 = __toESM(require_dist(), 1);
|
|
70160
|
-
command15 = new Command().description("schedule related commands").option("--json", "Output as JSON (for piping to jq)").action(list9).command("list", "list all schedules").option("--json", "Output as JSON (for piping to jq)").action(list9).command("get", "get a schedule's details").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(get7).command("new", "create a new schedule locally").arguments("<path:string>").action(newSchedule).command("push", "push a local schedule spec. This overrides any remote versions.").arguments("<file_path:string> <remote_path:string>").action(push8).command("enable", "Enable a schedule").arguments("<path:string>").action(enable).command("disable", "Disable a schedule").arguments("<path:string>").action(disable)
|
|
70633
|
+
command15 = new Command().description("schedule related commands").option("--json", "Output as JSON (for piping to jq)").action(list9).command("list", "list all schedules").option("--json", "Output as JSON (for piping to jq)").action(list9).command("get", "get a schedule's details").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(get7).command("new", "create a new schedule locally").arguments("<path:string>").action(newSchedule).command("push", "push a local schedule spec. This overrides any remote versions.").arguments("<file_path:string> <remote_path:string>").action(push8).command("enable", "Enable a schedule").arguments("<path:string>").action(enable).command("disable", "Disable a schedule").arguments("<path:string>").action(disable).command("set-permissioned-as", "Set the email (run-as user) for a schedule (requires admin or wm_deployers group)").arguments("<path:string> <email:string>").action(async (opts, schedulePath, email) => {
|
|
70634
|
+
const workspace = await resolveWorkspace(opts);
|
|
70635
|
+
await requireLogin(opts);
|
|
70636
|
+
const cache3 = new Map;
|
|
70637
|
+
const username = await lookupUsernameByEmail(workspace.workspaceId, email, cache3);
|
|
70638
|
+
const remote = await getSchedule({
|
|
70639
|
+
workspace: workspace.workspaceId,
|
|
70640
|
+
path: schedulePath
|
|
70641
|
+
});
|
|
70642
|
+
if (!remote)
|
|
70643
|
+
throw new Error(`Schedule ${schedulePath} not found`);
|
|
70644
|
+
await updateSchedule({
|
|
70645
|
+
workspace: workspace.workspaceId,
|
|
70646
|
+
path: schedulePath,
|
|
70647
|
+
requestBody: {
|
|
70648
|
+
...remote,
|
|
70649
|
+
permissioned_as: `u/${username}`,
|
|
70650
|
+
preserve_permissioned_as: true
|
|
70651
|
+
}
|
|
70652
|
+
});
|
|
70653
|
+
info(colors.green(`Updated permissioned_as for schedule ${schedulePath} to ${email} (username: ${username})`));
|
|
70654
|
+
});
|
|
70161
70655
|
schedule_default = command15;
|
|
70162
70656
|
});
|
|
70163
70657
|
|
|
@@ -71729,7 +72223,7 @@ async function createTrigger(triggerType, workspace, path18, trigger) {
|
|
|
71729
72223
|
const triggerFunction = triggerFunctions[triggerType];
|
|
71730
72224
|
await triggerFunction({ workspace, path: path18, requestBody: trigger });
|
|
71731
72225
|
}
|
|
71732
|
-
async function pushTrigger(triggerType, workspace, path18, trigger, localTrigger) {
|
|
72226
|
+
async function pushTrigger(triggerType, workspace, path18, trigger, localTrigger, permissionedAsContext) {
|
|
71733
72227
|
path18 = removeType(path18, triggerType + "_trigger").replaceAll(SEP17, "/");
|
|
71734
72228
|
debug(`Processing local ${triggerType} trigger ${path18}`);
|
|
71735
72229
|
try {
|
|
@@ -71738,6 +72232,17 @@ async function pushTrigger(triggerType, workspace, path18, trigger, localTrigger
|
|
|
71738
72232
|
} catch {
|
|
71739
72233
|
debug(`${triggerType} trigger ${path18} does not exist on remote`);
|
|
71740
72234
|
}
|
|
72235
|
+
delete localTrigger.has_permissioned_as;
|
|
72236
|
+
const preserveFields = {};
|
|
72237
|
+
if (permissionedAsContext?.userIsAdminOrDeployer) {
|
|
72238
|
+
if (trigger) {
|
|
72239
|
+
preserveFields.preserve_permissioned_as = true;
|
|
72240
|
+
if (trigger.permissioned_as) {
|
|
72241
|
+
preserveFields.permissioned_as = trigger.permissioned_as;
|
|
72242
|
+
info(`Preserving ${trigger.permissioned_as} as permissioned_as for trigger ${path18}`);
|
|
72243
|
+
}
|
|
72244
|
+
}
|
|
72245
|
+
}
|
|
71741
72246
|
if (trigger) {
|
|
71742
72247
|
if (isSuperset(localTrigger, trigger)) {
|
|
71743
72248
|
debug(`${triggerType} trigger ${path18} is up to date`);
|
|
@@ -71747,6 +72252,7 @@ async function pushTrigger(triggerType, workspace, path18, trigger, localTrigger
|
|
|
71747
72252
|
try {
|
|
71748
72253
|
await updateTrigger(triggerType, workspace, path18, {
|
|
71749
72254
|
...localTrigger,
|
|
72255
|
+
...preserveFields,
|
|
71750
72256
|
path: path18
|
|
71751
72257
|
});
|
|
71752
72258
|
} catch (e) {
|
|
@@ -71758,6 +72264,7 @@ async function pushTrigger(triggerType, workspace, path18, trigger, localTrigger
|
|
|
71758
72264
|
try {
|
|
71759
72265
|
await createTrigger(triggerType, workspace, path18, {
|
|
71760
72266
|
...localTrigger,
|
|
72267
|
+
...preserveFields,
|
|
71761
72268
|
path: path18
|
|
71762
72269
|
});
|
|
71763
72270
|
} catch (e) {
|
|
@@ -72116,7 +72623,29 @@ var init_trigger = __esm(async () => {
|
|
|
72116
72623
|
}
|
|
72117
72624
|
};
|
|
72118
72625
|
TRIGGER_SKIP_FIELDS = new Set(["workspace_id", "extra_perms", "edited_by", "edited_at"]);
|
|
72119
|
-
command19 = new Command().description("trigger related commands").option("--json", "Output as JSON (for piping to jq)").action(list11).command("list", "list all triggers").option("--json", "Output as JSON (for piping to jq)").action(list11).command("get", "get a trigger's details").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").option("--kind <kind:string>", "Trigger kind (http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, email). Recommended for faster lookup").action(get8).command("new", "create a new trigger locally").arguments("<path:string>").option("--kind <kind:string>", "Trigger kind (required: http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, email)").action(newTrigger).command("push", "push a local trigger spec. This overrides any remote versions.").arguments("<file_path:string> <remote_path:string>").action(push10)
|
|
72626
|
+
command19 = new Command().description("trigger related commands").option("--json", "Output as JSON (for piping to jq)").action(list11).command("list", "list all triggers").option("--json", "Output as JSON (for piping to jq)").action(list11).command("get", "get a trigger's details").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").option("--kind <kind:string>", "Trigger kind (http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, email). Recommended for faster lookup").action(get8).command("new", "create a new trigger locally").arguments("<path:string>").option("--kind <kind:string>", "Trigger kind (required: http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, email)").action(newTrigger).command("push", "push a local trigger spec. This overrides any remote versions.").arguments("<file_path:string> <remote_path:string>").action(push10).command("set-permissioned-as", "Set the email (run-as user) for a trigger (requires admin or wm_deployers group)").arguments("<path:string> <email:string>").option("--kind <kind:string>", "Trigger kind (required: http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, email)").action(async (opts, triggerPath, email) => {
|
|
72627
|
+
const workspace = await resolveWorkspace(opts);
|
|
72628
|
+
await requireLogin(opts);
|
|
72629
|
+
if (!opts.kind) {
|
|
72630
|
+
throw new Error("--kind is required. Valid kinds: " + TRIGGER_TYPES.join(", "));
|
|
72631
|
+
}
|
|
72632
|
+
if (!checkIfValidTrigger(opts.kind)) {
|
|
72633
|
+
throw new Error("Invalid trigger kind: " + opts.kind + ". Valid kinds: " + TRIGGER_TYPES.join(", "));
|
|
72634
|
+
}
|
|
72635
|
+
const { lookupUsernameByEmail: lookupUsernameByEmail2 } = await init_permissioned_as().then(() => exports_permissioned_as);
|
|
72636
|
+
const cache3 = new Map;
|
|
72637
|
+
const username = await lookupUsernameByEmail2(workspace.workspaceId, email, cache3);
|
|
72638
|
+
const remote = await getTrigger(opts.kind, workspace.workspaceId, triggerPath);
|
|
72639
|
+
if (!remote)
|
|
72640
|
+
throw new Error(`${opts.kind} trigger ${triggerPath} not found`);
|
|
72641
|
+
await updateTrigger(opts.kind, workspace.workspaceId, triggerPath, {
|
|
72642
|
+
...remote,
|
|
72643
|
+
permissioned_as: `u/${username}`,
|
|
72644
|
+
preserve_permissioned_as: true,
|
|
72645
|
+
path: triggerPath
|
|
72646
|
+
});
|
|
72647
|
+
info(colors.green(`Updated permissioned_as for ${opts.kind} trigger ${triggerPath} to ${email} (username: ${username})`));
|
|
72648
|
+
});
|
|
72120
72649
|
trigger_default = command19;
|
|
72121
72650
|
});
|
|
72122
72651
|
|
|
@@ -72173,14 +72702,14 @@ function showConflict(path19, local, remote) {
|
|
|
72173
72702
|
info(`
|
|
72174
72703
|
`);
|
|
72175
72704
|
}
|
|
72176
|
-
async function pushObj(workspace, p, befObj, newObj, plainSecrets, alreadySynced3, message, originalLocalPath) {
|
|
72705
|
+
async function pushObj(workspace, p, befObj, newObj, plainSecrets, alreadySynced3, message, originalLocalPath, permissionedAsContext) {
|
|
72177
72706
|
const typeEnding = getTypeStrFromPath(p);
|
|
72178
72707
|
if (typeEnding === "app") {
|
|
72179
72708
|
const appName = extractResourceName(p, "app");
|
|
72180
72709
|
if (!appName) {
|
|
72181
72710
|
throw new Error(`Could not extract app name from path: ${p}`);
|
|
72182
72711
|
}
|
|
72183
|
-
await pushApp(workspace, appName, buildFolderPath(appName, "app"), message);
|
|
72712
|
+
await pushApp(workspace, appName, buildFolderPath(appName, "app"), message, permissionedAsContext);
|
|
72184
72713
|
} else if (typeEnding === "raw_app") {
|
|
72185
72714
|
const rawAppName = extractResourceName(p, "raw_app");
|
|
72186
72715
|
if (!rawAppName) {
|
|
@@ -72196,7 +72725,7 @@ async function pushObj(workspace, p, befObj, newObj, plainSecrets, alreadySynced
|
|
|
72196
72725
|
if (!flowName) {
|
|
72197
72726
|
throw new Error(`Could not extract flow name from path: ${p}`);
|
|
72198
72727
|
}
|
|
72199
|
-
await pushFlow(workspace, flowName, buildFolderPath(flowName, "flow"), message);
|
|
72728
|
+
await pushFlow(workspace, flowName, buildFolderPath(flowName, "flow"), message, permissionedAsContext);
|
|
72200
72729
|
} else if (typeEnding === "resource") {
|
|
72201
72730
|
if (!alreadySynced3.includes(p)) {
|
|
72202
72731
|
alreadySynced3.push(p);
|
|
@@ -72205,25 +72734,25 @@ async function pushObj(workspace, p, befObj, newObj, plainSecrets, alreadySynced
|
|
|
72205
72734
|
} else if (typeEnding === "resource-type") {
|
|
72206
72735
|
await pushResourceType(workspace, p, befObj, newObj);
|
|
72207
72736
|
} else if (typeEnding === "schedule") {
|
|
72208
|
-
await pushSchedule(workspace, p, befObj, newObj);
|
|
72737
|
+
await pushSchedule(workspace, p, befObj, newObj, permissionedAsContext);
|
|
72209
72738
|
} else if (typeEnding === "http_trigger") {
|
|
72210
|
-
await pushTrigger("http", workspace, p, befObj, newObj);
|
|
72739
|
+
await pushTrigger("http", workspace, p, befObj, newObj, permissionedAsContext);
|
|
72211
72740
|
} else if (typeEnding === "websocket_trigger") {
|
|
72212
|
-
await pushTrigger("websocket", workspace, p, befObj, newObj);
|
|
72741
|
+
await pushTrigger("websocket", workspace, p, befObj, newObj, permissionedAsContext);
|
|
72213
72742
|
} else if (typeEnding === "kafka_trigger") {
|
|
72214
|
-
await pushTrigger("kafka", workspace, p, befObj, newObj);
|
|
72743
|
+
await pushTrigger("kafka", workspace, p, befObj, newObj, permissionedAsContext);
|
|
72215
72744
|
} else if (typeEnding === "nats_trigger") {
|
|
72216
|
-
await pushTrigger("nats", workspace, p, befObj, newObj);
|
|
72745
|
+
await pushTrigger("nats", workspace, p, befObj, newObj, permissionedAsContext);
|
|
72217
72746
|
} else if (typeEnding === "postgres_trigger") {
|
|
72218
|
-
await pushTrigger("postgres", workspace, p, befObj, newObj);
|
|
72747
|
+
await pushTrigger("postgres", workspace, p, befObj, newObj, permissionedAsContext);
|
|
72219
72748
|
} else if (typeEnding === "mqtt_trigger") {
|
|
72220
|
-
await pushTrigger("mqtt", workspace, p, befObj, newObj);
|
|
72749
|
+
await pushTrigger("mqtt", workspace, p, befObj, newObj, permissionedAsContext);
|
|
72221
72750
|
} else if (typeEnding === "sqs_trigger") {
|
|
72222
|
-
await pushTrigger("sqs", workspace, p, befObj, newObj);
|
|
72751
|
+
await pushTrigger("sqs", workspace, p, befObj, newObj, permissionedAsContext);
|
|
72223
72752
|
} else if (typeEnding === "gcp_trigger") {
|
|
72224
|
-
await pushTrigger("gcp", workspace, p, befObj, newObj);
|
|
72753
|
+
await pushTrigger("gcp", workspace, p, befObj, newObj, permissionedAsContext);
|
|
72225
72754
|
} else if (typeEnding === "email_trigger") {
|
|
72226
|
-
await pushTrigger("email", workspace, p, befObj, newObj);
|
|
72755
|
+
await pushTrigger("email", workspace, p, befObj, newObj, permissionedAsContext);
|
|
72227
72756
|
} else if (typeEnding === "native_trigger") {
|
|
72228
72757
|
await pushNativeTrigger(workspace, p, befObj, newObj);
|
|
72229
72758
|
} else if (typeEnding === "user") {
|
|
@@ -72547,7 +73076,7 @@ ${details.join(`
|
|
|
72547
73076
|
`)}
|
|
72548
73077
|
Use --remote to preview deployed workspace scripts instead.`);
|
|
72549
73078
|
}
|
|
72550
|
-
async function pushFlow(workspace, remotePath, localPath, message) {
|
|
73079
|
+
async function pushFlow(workspace, remotePath, localPath, message, permissionedAsContext) {
|
|
72551
73080
|
if (alreadySynced3.includes(localPath)) {
|
|
72552
73081
|
return;
|
|
72553
73082
|
}
|
|
@@ -72576,6 +73105,16 @@ async function pushFlow(workspace, remotePath, localPath, message) {
|
|
|
72576
73105
|
if (missingFiles.length > 0) {
|
|
72577
73106
|
warn(colors.yellow(`Warning: missing inline script file(s): ${missingFiles.join(", ")}. ` + `The flow will be pushed with unresolved !inline references.`));
|
|
72578
73107
|
}
|
|
73108
|
+
const hasOnBehalfOf = localFlow.has_on_behalf_of ?? !!localFlow.on_behalf_of_email;
|
|
73109
|
+
delete localFlow.has_on_behalf_of;
|
|
73110
|
+
const preserveFields = {};
|
|
73111
|
+
if (permissionedAsContext?.userIsAdminOrDeployer && hasOnBehalfOf) {
|
|
73112
|
+
if (flow && flow.on_behalf_of_email) {
|
|
73113
|
+
preserveFields.on_behalf_of_email = flow.on_behalf_of_email;
|
|
73114
|
+
preserveFields.preserve_on_behalf_of = true;
|
|
73115
|
+
info(`Preserving ${flow.on_behalf_of_email} as on_behalf_of for flow ${remotePath}`);
|
|
73116
|
+
}
|
|
73117
|
+
}
|
|
72579
73118
|
if (flow) {
|
|
72580
73119
|
if (isSuperset(localFlow, flow)) {
|
|
72581
73120
|
info(colors.green(`Flow ${remotePath} is up to date`));
|
|
@@ -72588,7 +73127,8 @@ async function pushFlow(workspace, remotePath, localPath, message) {
|
|
|
72588
73127
|
requestBody: {
|
|
72589
73128
|
path: remotePath.replaceAll(SEP19, "/"),
|
|
72590
73129
|
deployment_message: message,
|
|
72591
|
-
...localFlow
|
|
73130
|
+
...localFlow,
|
|
73131
|
+
...preserveFields
|
|
72592
73132
|
}
|
|
72593
73133
|
});
|
|
72594
73134
|
} else {
|
|
@@ -72599,7 +73139,8 @@ async function pushFlow(workspace, remotePath, localPath, message) {
|
|
|
72599
73139
|
requestBody: {
|
|
72600
73140
|
path: remotePath.replaceAll(SEP19, "/"),
|
|
72601
73141
|
deployment_message: message,
|
|
72602
|
-
...localFlow
|
|
73142
|
+
...localFlow,
|
|
73143
|
+
...preserveFields
|
|
72603
73144
|
}
|
|
72604
73145
|
});
|
|
72605
73146
|
} catch (e) {
|
|
@@ -73046,7 +73587,27 @@ var init_flow = __esm(async () => {
|
|
|
73046
73587
|
]);
|
|
73047
73588
|
import_yaml36 = __toESM(require_dist(), 1);
|
|
73048
73589
|
alreadySynced3 = [];
|
|
73049
|
-
command20 = new Command().description("flow related commands").option("--show-archived", "Enable archived flows in output").option("--json", "Output as JSON (for piping to jq)").action(list12).command("list", "list all flows").option("--show-archived", "Enable archived flows in output").option("--json", "Output as JSON (for piping to jq)").action(list12).command("get", "get a flow's details").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(get9).command("push", "push a local flow spec. This overrides any remote versions.").arguments("<file_path:string> <remote_path:string>").option("--message <message:string>", "Deployment message").action(push11).command("run", "run a flow by path.").arguments("<path:string>").option("-d --data <data:string>", "Inputs specified as a JSON string or a file using @<filename> or stdin using @-.").option("-s --silent", "Do not ouput anything other then the final output. Useful for scripting.").action(run3).command("preview", "preview a local flow without deploying it. Runs the flow definition from local files and uses local PathScripts by default.").arguments("<flow_path:string>").option("-d --data <data:string>", "Inputs specified as a JSON string or a file using @<filename> or stdin using @-.").option("-s --silent", "Do not output anything other then the final output. Useful for scripting.").option("--remote", "Use deployed workspace scripts for PathScript steps instead of local files.").action(preview2).command("generate-locks", 'DEPRECATED: re-generate flow lock files. Use "wmill generate-metadata" instead.').arguments("[flow:file]").option("--yes", "Skip confirmation prompt").option("--dry-run", "Perform a dry run without making changes").option("-i --includes <patterns:file[]>", "Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string)").option("-e --excludes <patterns:file[]>", "Comma separated patterns to specify which file to NOT take into account.").action(generateLocks).command("new", "create a new empty flow").arguments("<flow_path:string>").option("--summary <summary:string>", "flow summary").option("--description <description:string>", "flow description").action(bootstrap2).command("bootstrap", "create a new empty flow (alias for new)").arguments("<flow_path:string>").option("--summary <summary:string>", "flow summary").option("--description <description:string>", "flow description").action(bootstrap2).command("history", "Show version history for a flow").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(history2).command("show-version", "Show a specific version of a flow").arguments("<path:string> <version:string>").option("--json", "Output as JSON (for piping to jq)").action(showVersion)
|
|
73590
|
+
command20 = new Command().description("flow related commands").option("--show-archived", "Enable archived flows in output").option("--json", "Output as JSON (for piping to jq)").action(list12).command("list", "list all flows").option("--show-archived", "Enable archived flows in output").option("--json", "Output as JSON (for piping to jq)").action(list12).command("get", "get a flow's details").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(get9).command("push", "push a local flow spec. This overrides any remote versions.").arguments("<file_path:string> <remote_path:string>").option("--message <message:string>", "Deployment message").action(push11).command("run", "run a flow by path.").arguments("<path:string>").option("-d --data <data:string>", "Inputs specified as a JSON string or a file using @<filename> or stdin using @-.").option("-s --silent", "Do not ouput anything other then the final output. Useful for scripting.").action(run3).command("preview", "preview a local flow without deploying it. Runs the flow definition from local files and uses local PathScripts by default.").arguments("<flow_path:string>").option("-d --data <data:string>", "Inputs specified as a JSON string or a file using @<filename> or stdin using @-.").option("-s --silent", "Do not output anything other then the final output. Useful for scripting.").option("--remote", "Use deployed workspace scripts for PathScript steps instead of local files.").action(preview2).command("generate-locks", 'DEPRECATED: re-generate flow lock files. Use "wmill generate-metadata" instead.').arguments("[flow:file]").option("--yes", "Skip confirmation prompt").option("--dry-run", "Perform a dry run without making changes").option("-i --includes <patterns:file[]>", "Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string)").option("-e --excludes <patterns:file[]>", "Comma separated patterns to specify which file to NOT take into account.").action(generateLocks).command("new", "create a new empty flow").arguments("<flow_path:string>").option("--summary <summary:string>", "flow summary").option("--description <description:string>", "flow description").action(bootstrap2).command("bootstrap", "create a new empty flow (alias for new)").arguments("<flow_path:string>").option("--summary <summary:string>", "flow summary").option("--description <description:string>", "flow description").action(bootstrap2).command("history", "Show version history for a flow").arguments("<path:string>").option("--json", "Output as JSON (for piping to jq)").action(history2).command("show-version", "Show a specific version of a flow").arguments("<path:string> <version:string>").option("--json", "Output as JSON (for piping to jq)").action(showVersion).command("set-permissioned-as", "Set the on_behalf_of_email for a flow (requires admin or wm_deployers group)").arguments("<path:string> <email:string>").action(async (opts, flowPath, email) => {
|
|
73591
|
+
const workspace = await resolveWorkspace(opts);
|
|
73592
|
+
await requireLogin(opts);
|
|
73593
|
+
const remote = await getFlowByPath({
|
|
73594
|
+
workspace: workspace.workspaceId,
|
|
73595
|
+
path: flowPath
|
|
73596
|
+
});
|
|
73597
|
+
if (!remote)
|
|
73598
|
+
throw new Error(`Flow ${flowPath} not found`);
|
|
73599
|
+
await updateFlow({
|
|
73600
|
+
workspace: workspace.workspaceId,
|
|
73601
|
+
path: flowPath,
|
|
73602
|
+
requestBody: {
|
|
73603
|
+
...remote,
|
|
73604
|
+
path: flowPath,
|
|
73605
|
+
on_behalf_of_email: email,
|
|
73606
|
+
preserve_on_behalf_of: true
|
|
73607
|
+
}
|
|
73608
|
+
});
|
|
73609
|
+
info(colors.green(`Updated permissioned_as for flow ${flowPath} to ${email}`));
|
|
73610
|
+
});
|
|
73050
73611
|
flow_default = command20;
|
|
73051
73612
|
});
|
|
73052
73613
|
|
|
@@ -76044,7 +76605,59 @@ await __promiseAll([
|
|
|
76044
76605
|
init_workspace(),
|
|
76045
76606
|
init_resource_type()
|
|
76046
76607
|
]);
|
|
76047
|
-
import { stat as
|
|
76608
|
+
import { stat as stat18, writeFile as writeFile20, rm as rm4 } from "node:fs/promises";
|
|
76609
|
+
|
|
76610
|
+
// src/guidance/writer.ts
|
|
76611
|
+
import { cp, mkdir as mkdir13, readdir as readdir9, readFile as readFile17, stat as stat17, writeFile as writeFile19 } from "node:fs/promises";
|
|
76612
|
+
import { join as join17 } from "node:path";
|
|
76613
|
+
|
|
76614
|
+
// src/guidance/core.ts
|
|
76615
|
+
function generateAgentsMdContent(skillsReference) {
|
|
76616
|
+
return `# Windmill AI Agent Instructions
|
|
76617
|
+
|
|
76618
|
+
You are a helpful assistant that can help with Windmill scripts, flows, apps, and resources management.
|
|
76619
|
+
|
|
76620
|
+
## Important Notes
|
|
76621
|
+
- Every new entity MUST be created using the skills listed below.
|
|
76622
|
+
- Every modification of an entity MUST be done using the skills listed below.
|
|
76623
|
+
- User MUST be asked where to create the entity. It can be in its user folder, under u/{user_name} folder, or in a new folder, /f/{folder_name}/. folder_name and user_name must be provided by the user.
|
|
76624
|
+
|
|
76625
|
+
## Script Writing Guide
|
|
76626
|
+
|
|
76627
|
+
You MUST use the \`write-script-<language>\` skill to write or modify scripts in the language specified by the user. Use bun by default.
|
|
76628
|
+
|
|
76629
|
+
## Flow Writing Guide
|
|
76630
|
+
|
|
76631
|
+
You MUST use the \`write-flow\` skill to create or modify flows.
|
|
76632
|
+
|
|
76633
|
+
## Raw App Development
|
|
76634
|
+
|
|
76635
|
+
You MUST use the \`raw-app\` skill to create or modify raw apps.
|
|
76636
|
+
Whenever a new app needs to be created you MUST ask the user to run \`wmill app new\` in its terminal first.
|
|
76637
|
+
|
|
76638
|
+
## Triggers
|
|
76639
|
+
|
|
76640
|
+
You MUST use the \`triggers\` skill to configure HTTP routes, WebSocket, Kafka, NATS, SQS, MQTT, GCP, or Postgres CDC triggers.
|
|
76641
|
+
|
|
76642
|
+
## Schedules
|
|
76643
|
+
|
|
76644
|
+
You MUST use the \`schedules\` skill to configure cron schedules.
|
|
76645
|
+
|
|
76646
|
+
## Resources
|
|
76647
|
+
|
|
76648
|
+
You MUST use the \`resources\` skill to manage resource types and credentials.
|
|
76649
|
+
|
|
76650
|
+
## CLI Reference
|
|
76651
|
+
|
|
76652
|
+
You MUST use the \`cli-commands\` skill to use the CLI.
|
|
76653
|
+
|
|
76654
|
+
## Skills
|
|
76655
|
+
|
|
76656
|
+
For specific guidance, ALWAYS use the skills listed below.
|
|
76657
|
+
|
|
76658
|
+
${skillsReference}
|
|
76659
|
+
`;
|
|
76660
|
+
}
|
|
76048
76661
|
|
|
76049
76662
|
// src/guidance/skills.ts
|
|
76050
76663
|
var SKILLS = [
|
|
@@ -81148,6 +81761,7 @@ app related commands
|
|
|
81148
81761
|
- \`--fix\` - Attempt to fix common issues (not implemented yet)
|
|
81149
81762
|
- \`app new\` - create a new raw app from a template
|
|
81150
81763
|
- \`app generate-agents [app_folder:string]\` - regenerate AGENTS.md and DATATABLES.md from remote workspace
|
|
81764
|
+
- \`app set-permissioned-as <path:string> <email:string>\` - Set the on_behalf_of_email for an app (requires admin or wm_deployers group)
|
|
81151
81765
|
|
|
81152
81766
|
### audit
|
|
81153
81767
|
|
|
@@ -81230,6 +81844,7 @@ flow related commands
|
|
|
81230
81844
|
- \`--json\` - Output as JSON (for piping to jq)
|
|
81231
81845
|
- \`flow show-version <path:string> <version:string>\` - Show a specific version of a flow
|
|
81232
81846
|
- \`--json\` - Output as JSON (for piping to jq)
|
|
81847
|
+
- \`flow set-permissioned-as <path:string> <email:string>\` - Set the on_behalf_of_email for a flow (requires admin or wm_deployers group)
|
|
81233
81848
|
|
|
81234
81849
|
### folder
|
|
81235
81850
|
|
|
@@ -81249,6 +81864,9 @@ folder related commands
|
|
|
81249
81864
|
- \`folder push <name:string>\` - push a local folder to the remote by name. This overrides any remote versions.
|
|
81250
81865
|
- \`folder add-missing\` - create default folder.meta.yaml for all subdirectories of f/ that are missing one
|
|
81251
81866
|
- \`-y, --yes\` - skip confirmation prompt
|
|
81867
|
+
- \`folder show-rules <name:string>\` - Show default_permissioned_as rules for a folder. Use --test-path to see which rule matches a given item path.
|
|
81868
|
+
- \`--test-path <path:string>\` - Test which rule matches this item path (e.g. f/prod/jobs/my_script)
|
|
81869
|
+
- \`--json\` - Output as JSON
|
|
81252
81870
|
|
|
81253
81871
|
### generate-metadata
|
|
81254
81872
|
|
|
@@ -81472,6 +82090,7 @@ schedule related commands
|
|
|
81472
82090
|
- \`schedule push <file_path:string> <remote_path:string>\` - push a local schedule spec. This overrides any remote versions.
|
|
81473
82091
|
- \`schedule enable <path:string>\` - Enable a schedule
|
|
81474
82092
|
- \`schedule disable <path:string>\` - Disable a schedule
|
|
82093
|
+
- \`schedule set-permissioned-as <path:string> <email:string>\` - Set the email (run-as user) for a schedule (requires admin or wm_deployers group)
|
|
81475
82094
|
|
|
81476
82095
|
### script
|
|
81477
82096
|
|
|
@@ -81503,6 +82122,7 @@ script related commands
|
|
|
81503
82122
|
- \`script bootstrap <path:file> <language:string>\` - create a new script (alias for new)
|
|
81504
82123
|
- \`--summary <summary:string>\` - script summary
|
|
81505
82124
|
- \`--description <description:string>\` - script description
|
|
82125
|
+
- \`script set-permissioned-as <path:string> <email:string>\` - Set the on_behalf_of_email for a script (requires admin or wm_deployers group)
|
|
81506
82126
|
- \`script history <path:string>\` - show version history for a script
|
|
81507
82127
|
- \`--json\` - Output as JSON (for piping to jq)
|
|
81508
82128
|
|
|
@@ -81576,6 +82196,7 @@ sync local with a remote workspaces or the opposite (push or pull)
|
|
|
81576
82196
|
- \`--lint\` - Run lint validation before pushing
|
|
81577
82197
|
- \`--locks-required\` - Fail if scripts or flow inline scripts that need locks have no locks
|
|
81578
82198
|
- \`--auto-metadata\` - Automatically regenerate stale metadata (locks and schemas) before pushing
|
|
82199
|
+
- \`--accept-overriding-permissioned-as-with-self\` - Accept that items with a different permissioned_as will be updated with your own user
|
|
81579
82200
|
|
|
81580
82201
|
### token
|
|
81581
82202
|
|
|
@@ -81610,6 +82231,8 @@ trigger related commands
|
|
|
81610
82231
|
- \`trigger new <path:string>\` - create a new trigger locally
|
|
81611
82232
|
- \`--kind <kind:string>\` - Trigger kind (required: http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, email)
|
|
81612
82233
|
- \`trigger push <file_path:string> <remote_path:string>\` - push a local trigger spec. This overrides any remote versions.
|
|
82234
|
+
- \`trigger set-permissioned-as <path:string> <email:string>\` - Set the email (run-as user) for a trigger (requires admin or wm_deployers group)
|
|
82235
|
+
- \`--kind <kind:string>\` - Trigger kind (required: http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, email)
|
|
81613
82236
|
|
|
81614
82237
|
### user
|
|
81615
82238
|
|
|
@@ -82647,52 +83270,165 @@ var SCHEMA_MAPPINGS = {
|
|
|
82647
83270
|
]
|
|
82648
83271
|
};
|
|
82649
83272
|
|
|
82650
|
-
// src/guidance/
|
|
82651
|
-
|
|
82652
|
-
|
|
82653
|
-
|
|
82654
|
-
|
|
82655
|
-
|
|
82656
|
-
|
|
82657
|
-
|
|
82658
|
-
|
|
82659
|
-
|
|
82660
|
-
|
|
82661
|
-
|
|
82662
|
-
|
|
82663
|
-
|
|
82664
|
-
|
|
82665
|
-
|
|
82666
|
-
|
|
82667
|
-
|
|
82668
|
-
|
|
82669
|
-
|
|
82670
|
-
|
|
82671
|
-
|
|
82672
|
-
|
|
82673
|
-
|
|
82674
|
-
|
|
82675
|
-
|
|
82676
|
-
|
|
82677
|
-
|
|
82678
|
-
|
|
82679
|
-
|
|
82680
|
-
|
|
82681
|
-
|
|
82682
|
-
|
|
82683
|
-
|
|
82684
|
-
|
|
82685
|
-
|
|
82686
|
-
|
|
83273
|
+
// src/guidance/writer.ts
|
|
83274
|
+
var WMILL_INIT_AI_SKILLS_SOURCE_ENV = "WMILL_INIT_AI_SKILLS_SOURCE";
|
|
83275
|
+
var WMILL_INIT_AI_AGENTS_SOURCE_ENV = "WMILL_INIT_AI_AGENTS_SOURCE";
|
|
83276
|
+
var WMILL_INIT_AI_CLAUDE_SOURCE_ENV = "WMILL_INIT_AI_CLAUDE_SOURCE";
|
|
83277
|
+
var CLAUDE_MD_DEFAULT = `Instructions are in @AGENTS.md
|
|
83278
|
+
`;
|
|
83279
|
+
async function writeAiGuidanceFiles(options) {
|
|
83280
|
+
const nonDottedPaths = options.nonDottedPaths ?? true;
|
|
83281
|
+
const skillMetadata = options.skillsSourcePath ? await readSkillMetadataFromDirectory(options.skillsSourcePath) : getGeneratedSkillMetadata();
|
|
83282
|
+
const agentsWritten = await writeProjectGuidanceFile({
|
|
83283
|
+
targetPath: join17(options.targetDir, "AGENTS.md"),
|
|
83284
|
+
overwrite: options.overwriteProjectGuidance ?? false,
|
|
83285
|
+
content: options.agentsSourcePath != null ? await readFile17(options.agentsSourcePath, "utf8") : generateAgentsMdContent(buildSkillsReference(skillMetadata))
|
|
83286
|
+
});
|
|
83287
|
+
const claudeWritten = await writeProjectGuidanceFile({
|
|
83288
|
+
targetPath: join17(options.targetDir, "CLAUDE.md"),
|
|
83289
|
+
overwrite: options.overwriteProjectGuidance ?? false,
|
|
83290
|
+
content: options.claudeSourcePath != null ? await readFile17(options.claudeSourcePath, "utf8") : CLAUDE_MD_DEFAULT
|
|
83291
|
+
});
|
|
83292
|
+
if (options.skillsSourcePath) {
|
|
83293
|
+
await copySkillsFromSource(options.targetDir, options.skillsSourcePath);
|
|
83294
|
+
} else {
|
|
83295
|
+
await writeGeneratedSkills(options.targetDir, nonDottedPaths);
|
|
83296
|
+
}
|
|
83297
|
+
return {
|
|
83298
|
+
agentsWritten,
|
|
83299
|
+
claudeWritten,
|
|
83300
|
+
skillCount: skillMetadata.length
|
|
83301
|
+
};
|
|
83302
|
+
}
|
|
83303
|
+
function buildSkillsReference(skills) {
|
|
83304
|
+
return skills.map((skill) => `- \`.claude/skills/${skill.directoryName}/SKILL.md\` - ${skill.description}`).join(`
|
|
83305
|
+
`);
|
|
83306
|
+
}
|
|
83307
|
+
async function copySkillsFromSource(targetDir, skillsSourcePath) {
|
|
83308
|
+
const skillsDir = await ensureSkillsDirectory(targetDir);
|
|
83309
|
+
await copyDirectoryContents(skillsSourcePath, skillsDir);
|
|
83310
|
+
return await readSkillMetadataFromDirectory(skillsDir);
|
|
83311
|
+
}
|
|
83312
|
+
async function writeGeneratedSkills(targetDir, nonDottedPaths) {
|
|
83313
|
+
const skillsDir = await ensureSkillsDirectory(targetDir);
|
|
83314
|
+
await Promise.all(SKILLS.map(async (skill) => {
|
|
83315
|
+
const skillDir = join17(skillsDir, skill.name);
|
|
83316
|
+
await mkdir13(skillDir, { recursive: true });
|
|
83317
|
+
await writeFile19(join17(skillDir, "SKILL.md"), renderGeneratedSkillContent(skill.name, nonDottedPaths), "utf8");
|
|
83318
|
+
}));
|
|
83319
|
+
return SKILLS.map((skill) => ({
|
|
83320
|
+
...skill,
|
|
83321
|
+
directoryName: skill.name
|
|
83322
|
+
}));
|
|
83323
|
+
}
|
|
83324
|
+
function getGeneratedSkillMetadata() {
|
|
83325
|
+
return SKILLS.map((skill) => ({
|
|
83326
|
+
...skill,
|
|
83327
|
+
directoryName: skill.name
|
|
83328
|
+
}));
|
|
83329
|
+
}
|
|
83330
|
+
async function ensureSkillsDirectory(targetDir) {
|
|
83331
|
+
const skillsDir = join17(targetDir, ".claude", "skills");
|
|
83332
|
+
await mkdir13(skillsDir, { recursive: true });
|
|
83333
|
+
return skillsDir;
|
|
83334
|
+
}
|
|
83335
|
+
async function copyDirectoryContents(sourceDir, targetDir) {
|
|
83336
|
+
const entries = await readdir9(sourceDir, { withFileTypes: true });
|
|
83337
|
+
await Promise.all(entries.map(async (entry) => {
|
|
83338
|
+
await cp(join17(sourceDir, entry.name), join17(targetDir, entry.name), {
|
|
83339
|
+
recursive: true,
|
|
83340
|
+
force: true
|
|
83341
|
+
});
|
|
83342
|
+
}));
|
|
83343
|
+
}
|
|
83344
|
+
function renderGeneratedSkillContent(skillName, nonDottedPaths) {
|
|
83345
|
+
let skillContent = SKILL_CONTENT[skillName];
|
|
83346
|
+
if (!skillContent) {
|
|
83347
|
+
throw new Error(`Missing generated skill content for ${skillName}`);
|
|
83348
|
+
}
|
|
83349
|
+
if (nonDottedPaths) {
|
|
83350
|
+
skillContent = skillContent.replaceAll("{{FLOW_SUFFIX}}", "__flow").replaceAll("{{APP_SUFFIX}}", "__app").replaceAll("{{RAW_APP_SUFFIX}}", "__raw_app").replaceAll("{{INLINE_SCRIPT_NAMING}}", "Inline script files should NOT include `.inline_script.` in their names (e.g. use `a.ts`, not `a.inline_script.ts`).");
|
|
83351
|
+
} else {
|
|
83352
|
+
skillContent = skillContent.replaceAll("{{FLOW_SUFFIX}}", ".flow").replaceAll("{{APP_SUFFIX}}", ".app").replaceAll("{{RAW_APP_SUFFIX}}", ".raw_app").replaceAll("{{INLINE_SCRIPT_NAMING}}", "Inline script files use the `.inline_script.` naming convention (e.g. `a.inline_script.ts`).");
|
|
83353
|
+
}
|
|
83354
|
+
const schemaMappings = SCHEMA_MAPPINGS[skillName];
|
|
83355
|
+
if (!schemaMappings || schemaMappings.length === 0) {
|
|
83356
|
+
return skillContent;
|
|
83357
|
+
}
|
|
83358
|
+
const schemaDocs = schemaMappings.map((mapping) => {
|
|
83359
|
+
const schemaYaml = SCHEMAS[mapping.schemaKey];
|
|
83360
|
+
if (!schemaYaml) {
|
|
83361
|
+
return null;
|
|
83362
|
+
}
|
|
83363
|
+
return formatSchemaForMarkdown(schemaYaml, mapping.name, mapping.filePattern);
|
|
83364
|
+
}).filter((entry) => entry !== null);
|
|
83365
|
+
if (schemaDocs.length === 0) {
|
|
83366
|
+
return skillContent;
|
|
83367
|
+
}
|
|
83368
|
+
return `${skillContent}
|
|
82687
83369
|
|
|
82688
|
-
|
|
83370
|
+
${schemaDocs.join(`
|
|
82689
83371
|
|
|
82690
|
-
|
|
83372
|
+
`)}`;
|
|
83373
|
+
}
|
|
83374
|
+
async function readSkillMetadataFromDirectory(skillsDir) {
|
|
83375
|
+
const entries = await readdir9(skillsDir, { withFileTypes: true });
|
|
83376
|
+
const skills = [];
|
|
83377
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
83378
|
+
if (!entry.isDirectory()) {
|
|
83379
|
+
continue;
|
|
83380
|
+
}
|
|
83381
|
+
const skillPath = join17(skillsDir, entry.name, "SKILL.md");
|
|
83382
|
+
if (!await stat17(skillPath).catch(() => null)) {
|
|
83383
|
+
continue;
|
|
83384
|
+
}
|
|
83385
|
+
const content = await readFile17(skillPath, "utf8");
|
|
83386
|
+
skills.push(parseSkillMetadata(content, entry.name));
|
|
83387
|
+
}
|
|
83388
|
+
return skills;
|
|
83389
|
+
}
|
|
83390
|
+
function parseSkillMetadata(content, fallbackName) {
|
|
83391
|
+
const frontMatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
83392
|
+
if (!frontMatterMatch) {
|
|
83393
|
+
return {
|
|
83394
|
+
name: fallbackName,
|
|
83395
|
+
description: `Skill loaded from ${fallbackName}`,
|
|
83396
|
+
directoryName: fallbackName
|
|
83397
|
+
};
|
|
83398
|
+
}
|
|
83399
|
+
let name = fallbackName;
|
|
83400
|
+
let description = `Skill loaded from ${fallbackName}`;
|
|
83401
|
+
for (const line of frontMatterMatch[1].split(`
|
|
83402
|
+
`)) {
|
|
83403
|
+
const separatorIndex = line.indexOf(":");
|
|
83404
|
+
if (separatorIndex === -1) {
|
|
83405
|
+
continue;
|
|
83406
|
+
}
|
|
83407
|
+
const key = line.slice(0, separatorIndex).trim();
|
|
83408
|
+
const value = line.slice(separatorIndex + 1).trim();
|
|
83409
|
+
if (key === "name" && value) {
|
|
83410
|
+
name = value;
|
|
83411
|
+
} else if (key === "description" && value) {
|
|
83412
|
+
description = value;
|
|
83413
|
+
}
|
|
83414
|
+
}
|
|
83415
|
+
return { name, description, directoryName: fallbackName };
|
|
83416
|
+
}
|
|
83417
|
+
async function writeProjectGuidanceFile(options) {
|
|
83418
|
+
if (!options.overwrite && await stat17(options.targetPath).catch(() => null)) {
|
|
83419
|
+
return false;
|
|
83420
|
+
}
|
|
83421
|
+
await writeFile19(options.targetPath, options.content, "utf8");
|
|
83422
|
+
return true;
|
|
83423
|
+
}
|
|
83424
|
+
function formatSchemaForMarkdown(schemaYaml, schemaName, filePattern) {
|
|
83425
|
+
return `## ${schemaName} (\`${filePattern}\`)
|
|
82691
83426
|
|
|
82692
|
-
|
|
83427
|
+
Must be a YAML file that adheres to the following schema:
|
|
82693
83428
|
|
|
82694
|
-
|
|
82695
|
-
|
|
83429
|
+
\`\`\`yaml
|
|
83430
|
+
${schemaYaml.trim()}
|
|
83431
|
+
\`\`\``;
|
|
82696
83432
|
}
|
|
82697
83433
|
|
|
82698
83434
|
// src/commands/init/template.ts
|
|
@@ -82893,6 +83629,13 @@ var CONFIG_REFERENCE = [
|
|
|
82893
83629
|
description: "Use __flow/__app/__raw_app suffixes instead of .flow/.app/.raw_app",
|
|
82894
83630
|
inlineComment: "recommended for new projects"
|
|
82895
83631
|
},
|
|
83632
|
+
{
|
|
83633
|
+
name: "syncBehavior",
|
|
83634
|
+
type: "string",
|
|
83635
|
+
default: "v1",
|
|
83636
|
+
description: "Sync behavior version — controls ownership handling during push/pull (v1: preserve permissioned_as on update, strip on_behalf_of_email on pull)",
|
|
83637
|
+
inlineComment: "v1 enables ownership preservation"
|
|
83638
|
+
},
|
|
82896
83639
|
{
|
|
82897
83640
|
name: "codebases",
|
|
82898
83641
|
type: "array",
|
|
@@ -83156,19 +83899,10 @@ function formatConfigReferenceJson() {
|
|
|
83156
83899
|
}
|
|
83157
83900
|
|
|
83158
83901
|
// src/commands/init/init.ts
|
|
83159
|
-
function formatSchemaForMarkdown(schemaYaml, schemaName, filePattern) {
|
|
83160
|
-
return `## ${schemaName} (\`${filePattern}\`)
|
|
83161
|
-
|
|
83162
|
-
Must be a YAML file that adheres to the following schema:
|
|
83163
|
-
|
|
83164
|
-
\`\`\`yaml
|
|
83165
|
-
${schemaYaml.trim()}
|
|
83166
|
-
\`\`\``;
|
|
83167
|
-
}
|
|
83168
83902
|
async function initAction(opts) {
|
|
83169
83903
|
let didBindWorkspace = false;
|
|
83170
83904
|
let boundProfile;
|
|
83171
|
-
if (await
|
|
83905
|
+
if (await stat18("wmill.yaml").catch(() => null)) {
|
|
83172
83906
|
info("wmill.yaml already exists, skipping config generation");
|
|
83173
83907
|
} else {
|
|
83174
83908
|
const { isGitRepository: isGitRepository2, getCurrentGitBranch: getCurrentGitBranch2 } = await Promise.resolve().then(() => (init_git(), exports_git));
|
|
@@ -83240,7 +83974,7 @@ async function initAction(opts) {
|
|
|
83240
83974
|
boundProfile = activeWorkspace;
|
|
83241
83975
|
}
|
|
83242
83976
|
}
|
|
83243
|
-
await
|
|
83977
|
+
await writeFile20("wmill.yaml", generateCommentedTemplate(branchName, undefined, wsBindings), "utf-8");
|
|
83244
83978
|
info(colors.green("wmill.yaml created with default settings"));
|
|
83245
83979
|
if (wsBindings && wsBindings.length > 0) {
|
|
83246
83980
|
didBindWorkspace = true;
|
|
@@ -83312,58 +84046,21 @@ async function initAction(opts) {
|
|
|
83312
84046
|
nonDottedPaths = config.nonDottedPaths ?? true;
|
|
83313
84047
|
} catch {}
|
|
83314
84048
|
try {
|
|
83315
|
-
const
|
|
83316
|
-
|
|
83317
|
-
|
|
83318
|
-
|
|
83319
|
-
|
|
84049
|
+
const guidanceResult = await writeAiGuidanceFiles({
|
|
84050
|
+
targetDir: ".",
|
|
84051
|
+
nonDottedPaths,
|
|
84052
|
+
overwriteProjectGuidance: false,
|
|
84053
|
+
skillsSourcePath: process.env[WMILL_INIT_AI_SKILLS_SOURCE_ENV],
|
|
84054
|
+
agentsSourcePath: process.env[WMILL_INIT_AI_AGENTS_SOURCE_ENV],
|
|
84055
|
+
claudeSourcePath: process.env[WMILL_INIT_AI_CLAUDE_SOURCE_ENV]
|
|
84056
|
+
});
|
|
84057
|
+
if (guidanceResult.agentsWritten) {
|
|
83320
84058
|
info(colors.green("Created AGENTS.md"));
|
|
83321
84059
|
}
|
|
83322
|
-
if (
|
|
83323
|
-
await writeFile19("CLAUDE.md", `Instructions are in @AGENTS.md
|
|
83324
|
-
`, "utf-8");
|
|
84060
|
+
if (guidanceResult.claudeWritten) {
|
|
83325
84061
|
info(colors.green("Created CLAUDE.md"));
|
|
83326
84062
|
}
|
|
83327
|
-
|
|
83328
|
-
await mkdir13(".claude/skills", { recursive: true });
|
|
83329
|
-
await Promise.all(SKILLS.map(async (skill) => {
|
|
83330
|
-
const skillDir = `.claude/skills/${skill.name}`;
|
|
83331
|
-
await mkdir13(skillDir, { recursive: true });
|
|
83332
|
-
let skillContent = SKILL_CONTENT[skill.name];
|
|
83333
|
-
if (skillContent) {
|
|
83334
|
-
if (nonDottedPaths) {
|
|
83335
|
-
skillContent = skillContent.replaceAll("{{FLOW_SUFFIX}}", "__flow").replaceAll("{{APP_SUFFIX}}", "__app").replaceAll("{{RAW_APP_SUFFIX}}", "__raw_app").replaceAll("{{INLINE_SCRIPT_NAMING}}", "Inline script files should NOT include `.inline_script.` in their names (e.g. use `a.ts`, not `a.inline_script.ts`).");
|
|
83336
|
-
} else {
|
|
83337
|
-
skillContent = skillContent.replaceAll("{{FLOW_SUFFIX}}", ".flow").replaceAll("{{APP_SUFFIX}}", ".app").replaceAll("{{RAW_APP_SUFFIX}}", ".raw_app").replaceAll("{{INLINE_SCRIPT_NAMING}}", "Inline script files use the `.inline_script.` naming convention (e.g. `a.inline_script.ts`).");
|
|
83338
|
-
}
|
|
83339
|
-
const schemaMappings = SCHEMA_MAPPINGS[skill.name];
|
|
83340
|
-
if (schemaMappings && schemaMappings.length > 0) {
|
|
83341
|
-
const schemaDocs = schemaMappings.map((mapping) => {
|
|
83342
|
-
const schemaYaml = SCHEMAS[mapping.schemaKey];
|
|
83343
|
-
if (schemaYaml) {
|
|
83344
|
-
return formatSchemaForMarkdown(schemaYaml, mapping.name, mapping.filePattern);
|
|
83345
|
-
}
|
|
83346
|
-
return null;
|
|
83347
|
-
}).filter((doc) => doc !== null);
|
|
83348
|
-
if (schemaDocs.length > 0) {
|
|
83349
|
-
skillContent = skillContent + `
|
|
83350
|
-
|
|
83351
|
-
` + schemaDocs.join(`
|
|
83352
|
-
|
|
83353
|
-
`);
|
|
83354
|
-
}
|
|
83355
|
-
}
|
|
83356
|
-
await writeFile19(`${skillDir}/SKILL.md`, skillContent, "utf-8");
|
|
83357
|
-
}
|
|
83358
|
-
}));
|
|
83359
|
-
info(colors.green(`Created .claude/skills/ with ${SKILLS.length} skills`));
|
|
83360
|
-
} catch (skillError) {
|
|
83361
|
-
if (skillError instanceof Error) {
|
|
83362
|
-
warn(`Could not create skills: ${skillError.message}`);
|
|
83363
|
-
} else {
|
|
83364
|
-
warn(`Could not create skills: ${skillError}`);
|
|
83365
|
-
}
|
|
83366
|
-
}
|
|
84063
|
+
info(colors.green(`Created .claude/skills/ with ${guidanceResult.skillCount} skills`));
|
|
83367
84064
|
} catch (error2) {
|
|
83368
84065
|
if (error2 instanceof Error) {
|
|
83369
84066
|
warn(`Could not create guidance files: ${error2.message}`);
|
|
@@ -84406,7 +85103,7 @@ init_mod3();
|
|
|
84406
85103
|
init_log();
|
|
84407
85104
|
init_colors2();
|
|
84408
85105
|
var import_yaml40 = __toESM(require_dist(), 1);
|
|
84409
|
-
import { writeFile as
|
|
85106
|
+
import { writeFile as writeFile22 } from "node:fs/promises";
|
|
84410
85107
|
init_yaml();
|
|
84411
85108
|
await init_conf();
|
|
84412
85109
|
async function configAction(opts) {
|
|
@@ -84458,7 +85155,7 @@ async function migrateAction() {
|
|
|
84458
85155
|
} else {
|
|
84459
85156
|
newConf.workspaces = workspaces;
|
|
84460
85157
|
}
|
|
84461
|
-
await
|
|
85158
|
+
await writeFile22(wmillYamlPath, import_yaml40.stringify(newConf), "utf-8");
|
|
84462
85159
|
info(colors.green(`✅ Migrated '${legacyKey}' to 'workspaces' in ${wmillYamlPath}`));
|
|
84463
85160
|
if (wsNames.length > 0) {
|
|
84464
85161
|
info(` Workspace entries: ${wsNames.join(", ")}`);
|
|
@@ -84472,8 +85169,36 @@ var config_default = command35;
|
|
|
84472
85169
|
|
|
84473
85170
|
// src/main.ts
|
|
84474
85171
|
await init_context();
|
|
84475
|
-
var VERSION = "1.
|
|
84476
|
-
|
|
85172
|
+
var VERSION = "1.683.0";
|
|
85173
|
+
async function checkVersionSafe(cmd) {
|
|
85174
|
+
const mainCommand = cmd.getMainCommand();
|
|
85175
|
+
const upgradeCommand = mainCommand.getCommand("upgrade");
|
|
85176
|
+
if (!upgradeCommand || typeof upgradeCommand.getLatestVersion !== "function" || typeof upgradeCommand.hasRequiredPermissions !== "function") {
|
|
85177
|
+
return;
|
|
85178
|
+
}
|
|
85179
|
+
if (!await upgradeCommand.hasRequiredPermissions()) {
|
|
85180
|
+
return;
|
|
85181
|
+
}
|
|
85182
|
+
const latestVersion = await upgradeCommand.getLatestVersion();
|
|
85183
|
+
const currentVersion = mainCommand.getVersion();
|
|
85184
|
+
if (!currentVersion || currentVersion === latestVersion) {
|
|
85185
|
+
return;
|
|
85186
|
+
}
|
|
85187
|
+
mainCommand.version(`${currentVersion} (New version available: ${latestVersion}. Run '${mainCommand.getName()} upgrade' to upgrade to the latest version!)`);
|
|
85188
|
+
}
|
|
85189
|
+
var command36 = new Command().name("wmill").action(() => info(`Welcome to Windmill CLI ${VERSION}. Use -h for help.`)).description("Windmill CLI").globalOption("--workspace <workspace:string>", "Specify the target workspace. This overrides the default workspace.").globalOption("--debug --verbose", "Show debug/verbose logs").globalOption("--show-diffs", "Show diff informations when syncing (may show sensitive informations)").globalOption("--token <token:string>", "Specify an API token. This will override any stored token.").globalOption("--base-url <baseUrl:string>", "Specify the base URL of the API. If used, --token and --workspace are required and no local remote/workspace already set will be used.").globalOption("--config-dir <configDir:string>", "Specify a custom config directory. Overrides WMILL_CONFIG_DIR environment variable and default ~/.config location.").env("HEADERS <headers:string>", `Specify headers to use for all requests. e.g: "HEADERS='h1: v1, h2: v2'"`).version(VERSION).versionOption(false).helpOption("-h, --help", "Show this help.", {
|
|
85190
|
+
action: async function() {
|
|
85191
|
+
const self2 = this;
|
|
85192
|
+
const long = self2.getRawArgs().includes(`--${self2.getHelpOption()?.name}`);
|
|
85193
|
+
try {
|
|
85194
|
+
await checkVersionSafe(self2);
|
|
85195
|
+
} catch (e) {
|
|
85196
|
+
warn(`Skipping latest-version check: ${e instanceof Error ? e.message : String(e)}`);
|
|
85197
|
+
}
|
|
85198
|
+
self2.showHelp({ long });
|
|
85199
|
+
self2.exit();
|
|
85200
|
+
}
|
|
85201
|
+
}).command("init", init_default).command("app", app_default).command("flow", flow_default).command("script", script_default).command("workspace", workspace_default).command("resource", resource_default).command("resource-type", resource_type_default).command("user", user_default).command("variable", variable_default).command("hub", hub_default).command("folder", folder_default).command("schedule", schedule_default).command("trigger", trigger_default).command("dev", dev_default2).command("sync", sync_default).command("lint", lint_default).command("gitsync-settings", gitsync_settings_default).command("instance", instance_default).command("worker-groups", worker_groups_default).command("workers", workers_default).command("queues", queues_default).command("dependencies", dependencies_default).command("jobs", jobs_default).command("job", job_default).command("group", group_default).command("audit", audit_default).command("token", token_default).command("generate-metadata", generate_metadata_default).command("docs", docs_default).command("config", config_default).command("version --version", "Show version information").action(async (opts) => {
|
|
84477
85202
|
console.log("CLI version: " + VERSION);
|
|
84478
85203
|
try {
|
|
84479
85204
|
const provider2 = new NpmProvider({ package: "windmill-cli" });
|