@ai-setting/roy-agent-core 1.7.53 → 1.7.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/index.js +7 -7
- package/dist/env/agent/agent-component.d.ts +30 -0
- package/dist/env/agent/index.js +13 -12
- package/dist/env/commands/index.js +7 -7
- package/dist/env/context/index.js +3 -3
- package/dist/env/debug/index.js +7 -7
- package/dist/env/environment.d.ts +7 -0
- package/dist/env/error-reply.d.ts +184 -0
- package/dist/env/event-source/event-source-handlers.d.ts +9 -23
- package/dist/env/event-source/index.js +17 -17
- package/dist/env/hook/index.js +5 -5
- package/dist/env/index.js +41 -40
- package/dist/env/llm/adapter-ai-sdk.d.ts +6 -0
- package/dist/env/llm/adapter-pi-ai.d.ts +152 -2
- package/dist/env/llm/adapter.d.ts +21 -1
- package/dist/env/llm/index.js +10 -10
- package/dist/env/llm/invoke.d.ts +74 -2
- package/dist/env/llm/llm.d.ts +38 -0
- package/dist/env/llm/outbound-defense.d.ts +140 -0
- package/dist/env/llm/reasoning-defense.d.ts +104 -0
- package/dist/env/llm/types.d.ts +26 -0
- package/dist/env/log-trace/decorator.d.ts +46 -3
- package/dist/env/log-trace/index.d.ts +1 -0
- package/dist/env/log-trace/index.js +33 -7
- package/dist/env/log-trace/trace-filters.d.ts +127 -0
- package/dist/env/mcp/index.js +7 -7
- package/dist/env/memory/index.js +7 -7
- package/dist/env/plugin/index.js +7 -7
- package/dist/env/prompt/index.js +8 -8
- package/dist/env/prompt/prompts-index.d.ts +1 -1
- package/dist/env/session/index.js +11 -10
- package/dist/env/session/session-component.d.ts +4 -0
- package/dist/env/session/storage/index.js +5 -3
- package/dist/env/skill/index.js +7 -7
- package/dist/env/task/backend/index.js +0 -2
- package/dist/env/task/backend/types.d.ts +0 -11
- package/dist/env/task/delegate/index.js +8 -8
- package/dist/env/task/index.js +13 -13
- package/dist/env/task/plugins/card-chat-routing.d.ts +42 -89
- package/dist/env/task/plugins/index.js +10 -10
- package/dist/env/task/plugins/lark-card-patch-notifier.d.ts +0 -51
- package/dist/env/task/plugins/lark-cli-task-notify-plugin.d.ts +14 -31
- package/dist/env/task/plugins/per-task-group-chat-plugin.d.ts +4 -4
- package/dist/env/task/storage/index.js +2 -2
- package/dist/env/task/tools/index.js +3 -3
- package/dist/env/tool/built-in/index.js +3 -3
- package/dist/env/tool/index.js +9 -9
- package/dist/env/workflow/engine/engine.d.ts +65 -0
- package/dist/env/workflow/engine/index.js +12 -12
- package/dist/env/workflow/hooks/index.js +7 -7
- package/dist/env/workflow/index.js +23 -22
- package/dist/env/workflow/nodes/index.js +2 -2
- package/dist/env/workflow/nodes/tool-node.d.ts +6 -0
- package/dist/env/workflow/plugins/index.js +8 -8
- package/dist/env/workflow/service/index.js +2 -2
- package/dist/env/workflow/storage/index.js +3 -3
- package/dist/env/workflow/tools/index.js +15 -14
- package/dist/env/workflow/tools/workflow-validate-tool.d.ts +11 -2
- package/dist/env/workflow/types/run.d.ts +19 -0
- package/dist/env/workflow/utils/agent-session-metadata.d.ts +13 -0
- package/dist/env/workflow/utils/index.js +5 -5
- package/dist/env/workflow/utils/redact-command-secrets.d.ts +70 -0
- package/dist/env/workflow/utils/shell-dialect-lint.d.ts +75 -0
- package/dist/env/workflow/utils/validate-workflow-definition.d.ts +22 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +82 -56
- package/dist/shared/@ai-setting/{roy-agent-core-k25gv86p.js → roy-agent-core-0b3fdnn3.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-vvxb9522.js → roy-agent-core-0bmk44ye.js} +71 -10
- package/dist/shared/@ai-setting/{roy-agent-core-k8b440n3.js → roy-agent-core-16t0zfbk.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-4z3rq5gw.js → roy-agent-core-1pnfkspm.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-na73mym1.js → roy-agent-core-202v00x4.js} +213 -2
- package/dist/shared/@ai-setting/{roy-agent-core-ph5qbqcx.js → roy-agent-core-2044fbsd.js} +471 -134
- package/dist/shared/@ai-setting/{roy-agent-core-fs36xgcs.js → roy-agent-core-3dkzqqsd.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-qm9jda8p.js → roy-agent-core-3kz6934b.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-tjg71zqp.js → roy-agent-core-3mc6vmvj.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-tdbv7ff4.js → roy-agent-core-40xzbjtm.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-athdr2m0.js → roy-agent-core-4zy2v3br.js} +10 -2
- package/dist/shared/@ai-setting/{roy-agent-core-t37wbysj.js → roy-agent-core-5gw6w424.js} +4 -4
- package/dist/shared/@ai-setting/{roy-agent-core-978b6wfx.js → roy-agent-core-5psjwd08.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-7evvpfwc.js → roy-agent-core-5rxs0ra3.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-gdsqp6j8.js → roy-agent-core-6masakz4.js} +45 -0
- package/dist/shared/@ai-setting/{roy-agent-core-stnddvx9.js → roy-agent-core-6qgq7h1g.js} +4 -4
- package/dist/shared/@ai-setting/{roy-agent-core-6167phd4.js → roy-agent-core-833t16n1.js} +3 -3
- package/dist/shared/@ai-setting/{roy-agent-core-2k4xffaa.js → roy-agent-core-8cgtwytv.js} +3 -3
- package/dist/shared/@ai-setting/{roy-agent-core-rx16h2m1.js → roy-agent-core-97eyghsp.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-fvs48d38.js → roy-agent-core-98aa9r0x.js} +12 -7
- package/dist/shared/@ai-setting/{roy-agent-core-3tv1q6f2.js → roy-agent-core-9nv586rw.js} +57 -19
- package/dist/shared/@ai-setting/roy-agent-core-b599jgyd.js +578 -0
- package/dist/shared/@ai-setting/{roy-agent-core-yd21n6c7.js → roy-agent-core-bwzy00t2.js} +12 -5
- package/dist/shared/@ai-setting/{roy-agent-core-dwx9hjwx.js → roy-agent-core-c6at31ra.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-1cv9j4dg.js → roy-agent-core-d14h726p.js} +3 -3
- package/dist/shared/@ai-setting/{roy-agent-core-4sx6sdb2.js → roy-agent-core-d4jpn06f.js} +22 -9
- package/dist/shared/@ai-setting/{roy-agent-core-39xejr39.js → roy-agent-core-e0wbq0qq.js} +7 -7
- package/dist/shared/@ai-setting/{roy-agent-core-vrsxhw9e.js → roy-agent-core-e677am8e.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-hch2geta.js → roy-agent-core-ezsyyjxw.js} +21 -190
- package/dist/shared/@ai-setting/{roy-agent-core-ne2t1pay.js → roy-agent-core-f8sp1txg.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-ak4dc3kg.js → roy-agent-core-gn8f4rhs.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-qcspfszy.js → roy-agent-core-gngjkw3b.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-9jzysq50.js → roy-agent-core-heft3j4c.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-5apmk4qw.js → roy-agent-core-jszgaevt.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-nde5gzta.js → roy-agent-core-m8n0dffe.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-x8n1r7h4.js → roy-agent-core-n3b7ztms.js} +189 -8
- package/dist/shared/@ai-setting/{roy-agent-core-4vpk40ss.js → roy-agent-core-nhk0dsxr.js} +13 -13
- package/dist/shared/@ai-setting/{roy-agent-core-j8e92gj8.js → roy-agent-core-nntphgww.js} +1 -60
- package/dist/shared/@ai-setting/{roy-agent-core-8s9jd2qy.js → roy-agent-core-nra8vfrs.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-pk1b3kvf.js → roy-agent-core-ns3pbyz4.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-7g03zkkb.js → roy-agent-core-pemxr5n4.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-skzjs973.js → roy-agent-core-pjxa78mp.js} +5 -5
- package/dist/shared/@ai-setting/{roy-agent-core-p8ak03s0.js → roy-agent-core-q7zksbw0.js} +117 -315
- package/dist/shared/@ai-setting/{roy-agent-core-zd3ddm4k.js → roy-agent-core-qjjf4tjb.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-qamktze3.js → roy-agent-core-qyqnmz8c.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-9csa89jx.js → roy-agent-core-r9qe0ghe.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-h93v6p6y.js → roy-agent-core-tc7gw38m.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-35s1w7tc.js → roy-agent-core-v2188rnk.js} +157 -85
- package/dist/shared/@ai-setting/{roy-agent-core-p2r27era.js → roy-agent-core-vmg30n5x.js} +8 -8
- package/dist/shared/@ai-setting/{roy-agent-core-rnwr39b0.js → roy-agent-core-w7hnjhvx.js} +5 -3
- package/dist/shared/@ai-setting/{roy-agent-core-dv714bqh.js → roy-agent-core-wafk25ff.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-brwzs8wf.js → roy-agent-core-xcq04fq0.js} +5 -5
- package/dist/shared/@ai-setting/{roy-agent-core-2gay3sjt.js → roy-agent-core-yg247mk4.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-zy3hqfwg.js → roy-agent-core-yv9x4wmg.js} +631 -35
- package/dist/shared/@ai-setting/{roy-agent-core-yhsssn86.js → roy-agent-core-zmeka63k.js} +1 -1
- package/dist/test-utils/child-process-mock.d.ts +73 -0
- package/package.json +2 -2
- package/dist/shared/@ai-setting/roy-agent-core-dsevjbck.js +0 -245
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isValidSessionId
|
|
3
|
-
} from "./roy-agent-core-
|
|
3
|
+
} from "./roy-agent-core-bwzy00t2.js";
|
|
4
4
|
import {
|
|
5
5
|
TAG_MAX_COUNT,
|
|
6
6
|
TAG_MIN_COUNT
|
|
7
|
-
} from "./roy-agent-core-
|
|
7
|
+
} from "./roy-agent-core-5rxs0ra3.js";
|
|
8
8
|
import {
|
|
9
9
|
validateWorkflowDefinition
|
|
10
|
-
} from "./roy-agent-core-
|
|
10
|
+
} from "./roy-agent-core-202v00x4.js";
|
|
11
11
|
import {
|
|
12
12
|
getCurrentBgTaskId
|
|
13
13
|
} from "./roy-agent-core-c1v263jn.js";
|
|
@@ -18,13 +18,14 @@ import {
|
|
|
18
18
|
import {
|
|
19
19
|
TracedAs,
|
|
20
20
|
init_decorator
|
|
21
|
-
} from "./roy-agent-core-
|
|
21
|
+
} from "./roy-agent-core-b599jgyd.js";
|
|
22
22
|
import {
|
|
23
23
|
createLogger,
|
|
24
24
|
init_logger
|
|
25
25
|
} from "./roy-agent-core-8seq65bp.js";
|
|
26
26
|
import {
|
|
27
|
-
__legacyDecorateClassTS
|
|
27
|
+
__legacyDecorateClassTS,
|
|
28
|
+
__require
|
|
28
29
|
} from "./roy-agent-core-fs0mn2jk.js";
|
|
29
30
|
|
|
30
31
|
// src/env/workflow/tools/ask-user-tool.ts
|
|
@@ -480,8 +481,25 @@ var logger5 = createLogger("workflow-validate-tool");
|
|
|
480
481
|
var WorkflowValidateInputSchema = z6.object({
|
|
481
482
|
definition: z6.any().optional().describe("Workflow definition object (alternative to definition_yaml)"),
|
|
482
483
|
definition_yaml: z6.string().optional().describe("Workflow definition as YAML or JSON string"),
|
|
483
|
-
yaml: z6.string().optional().describe("Alias for definition_yaml — workflow definition as YAML or JSON string")
|
|
484
|
+
yaml: z6.string().optional().describe("Alias for definition_yaml — workflow definition as YAML or JSON string"),
|
|
485
|
+
file: z6.string().optional().describe("Path to a YAML/JSON file to validate. Use this INSTEAD of passing " + "`$(cat /path)` — shell substitutions are NOT expanded by this tool.")
|
|
484
486
|
});
|
|
487
|
+
function isAllowedPath(absPath) {
|
|
488
|
+
const cwd = process.cwd();
|
|
489
|
+
const normalized = absPath.replace(/\\/g, "/");
|
|
490
|
+
if (normalized === cwd || normalized.startsWith(`${cwd}/`))
|
|
491
|
+
return true;
|
|
492
|
+
if (normalized === "/tmp" || normalized.startsWith("/tmp/"))
|
|
493
|
+
return true;
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
function detectShellSubstitution(text) {
|
|
497
|
+
const trimmed = text.trim();
|
|
498
|
+
const looksLikeSubstitution = /^\$\(.*\)$/.test(trimmed) || /^`.*`$/.test(trimmed) || /\$\(\s*cat\s+/.test(trimmed) || /`\s*cat\s+/.test(trimmed);
|
|
499
|
+
if (!looksLikeSubstitution)
|
|
500
|
+
return;
|
|
501
|
+
return "Shell substitutions such as `$(cat /path)` are NOT expanded by this tool — the " + "string is passed to the YAML parser verbatim, which is why you saw " + "`Parsed definition is not an object`. Pass the path directly instead: " + 'workflow_validate({ file: "/tmp/x.yaml" }).';
|
|
502
|
+
}
|
|
485
503
|
function createWorkflowValidateTool(_workflowService) {
|
|
486
504
|
return {
|
|
487
505
|
name: "workflow_validate",
|
|
@@ -493,13 +511,56 @@ function createWorkflowValidateTool(_workflowService) {
|
|
|
493
511
|
let definition;
|
|
494
512
|
if (params.definition && typeof params.definition === "object") {
|
|
495
513
|
definition = params.definition;
|
|
514
|
+
} else if (params.file && typeof params.file === "string") {
|
|
515
|
+
const fs = await import("node:fs/promises");
|
|
516
|
+
const nodePath = await import("node:path");
|
|
517
|
+
const abs = nodePath.resolve(params.file);
|
|
518
|
+
if (!isAllowedPath(abs)) {
|
|
519
|
+
return {
|
|
520
|
+
success: false,
|
|
521
|
+
output: "",
|
|
522
|
+
error: `Refusing to read outside the working directory or /tmp: ${abs}. Copy the file into CWD or /tmp first.`,
|
|
523
|
+
metadata: { execution_time_ms: 0 }
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
let raw;
|
|
527
|
+
try {
|
|
528
|
+
raw = await fs.readFile(abs, "utf-8");
|
|
529
|
+
} catch (readErr) {
|
|
530
|
+
const code = readErr?.code;
|
|
531
|
+
return {
|
|
532
|
+
success: false,
|
|
533
|
+
output: "",
|
|
534
|
+
error: code === "ENOENT" ? `Workflow file not found: ${abs}. Check the path (it is resolved against CWD: ${process.cwd()}).` : `Failed to read workflow file ${abs}: ${readErr instanceof Error ? readErr.message : String(readErr)}`,
|
|
535
|
+
metadata: { execution_time_ms: 0 }
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
try {
|
|
539
|
+
definition = yaml2.parse(raw);
|
|
540
|
+
} catch (yamlErr) {
|
|
541
|
+
return {
|
|
542
|
+
success: false,
|
|
543
|
+
output: "",
|
|
544
|
+
error: `Invalid YAML in ${abs}: ${yamlErr instanceof Error ? yamlErr.message : String(yamlErr)}`,
|
|
545
|
+
metadata: { execution_time_ms: 0 }
|
|
546
|
+
};
|
|
547
|
+
}
|
|
496
548
|
} else {
|
|
497
549
|
const yamlText = params.yaml ?? params.definition_yaml;
|
|
498
550
|
if (!yamlText || typeof yamlText !== "string") {
|
|
499
551
|
return {
|
|
500
552
|
success: false,
|
|
501
553
|
output: "",
|
|
502
|
-
error: "Must provide either 'definition' (object), 'definition_yaml', or 'yaml' (string)",
|
|
554
|
+
error: "Must provide either 'definition' (object), 'file' (path), 'definition_yaml', or 'yaml' (string)",
|
|
555
|
+
metadata: { execution_time_ms: 0 }
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
const substitutionHint = detectShellSubstitution(yamlText);
|
|
559
|
+
if (substitutionHint) {
|
|
560
|
+
return {
|
|
561
|
+
success: false,
|
|
562
|
+
output: "",
|
|
563
|
+
error: substitutionHint,
|
|
503
564
|
metadata: { execution_time_ms: 0 }
|
|
504
565
|
};
|
|
505
566
|
}
|
|
@@ -518,7 +579,7 @@ function createWorkflowValidateTool(_workflowService) {
|
|
|
518
579
|
return {
|
|
519
580
|
success: false,
|
|
520
581
|
output: "",
|
|
521
|
-
error:
|
|
582
|
+
error: `Parsed definition is not an object (got ${definition === null ? "null" : typeof definition}). If you passed a file path as a string, use workflow_validate({ file: "/path/to.yaml" }) ` + "instead of embedding `$(cat ...)` — shell substitutions are not expanded here.",
|
|
522
583
|
metadata: { execution_time_ms: 0 }
|
|
523
584
|
};
|
|
524
585
|
}
|
|
@@ -803,7 +864,7 @@ class RunWorkflowToolRunner {
|
|
|
803
864
|
__legacyDecorateClassTS([
|
|
804
865
|
TracedAs("workflow.run-workflow-tool.execute", {
|
|
805
866
|
recordParams: true,
|
|
806
|
-
recordResult:
|
|
867
|
+
recordResult: false
|
|
807
868
|
})
|
|
808
869
|
], RunWorkflowToolRunner.prototype, "execute", null);
|
|
809
870
|
|
|
@@ -845,7 +906,7 @@ class RunWorkflowLifecycleTracer {
|
|
|
845
906
|
__legacyDecorateClassTS([
|
|
846
907
|
TracedAs("workflow.run-tool.pre-attach", {
|
|
847
908
|
recordParams: true,
|
|
848
|
-
recordResult:
|
|
909
|
+
recordResult: false
|
|
849
910
|
})
|
|
850
911
|
], RunWorkflowLifecycleTracer.prototype, "execute", null);
|
|
851
912
|
function createRunWorkflowTool(workflowService, hooks) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
init_context
|
|
3
|
-
} from "./roy-agent-core-
|
|
3
|
+
} from "./roy-agent-core-16t0zfbk.js";
|
|
4
4
|
import {
|
|
5
5
|
getEnvContext
|
|
6
|
-
} from "./roy-agent-core-
|
|
6
|
+
} from "./roy-agent-core-jszgaevt.js";
|
|
7
7
|
import {
|
|
8
8
|
__esm
|
|
9
9
|
} from "./roy-agent-core-fs0mn2jk.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DAGManager,
|
|
3
3
|
init_dag_manager
|
|
4
|
-
} from "./roy-agent-core-
|
|
4
|
+
} from "./roy-agent-core-nra8vfrs.js";
|
|
5
5
|
|
|
6
6
|
// src/env/workflow/utils/node-output-contracts.ts
|
|
7
7
|
var VIRTUAL_OUTPUT_FIELD = "output";
|
|
@@ -315,6 +315,167 @@ function formatTemplateContractFindings(findings) {
|
|
|
315
315
|
|
|
316
316
|
// src/env/workflow/utils/validate-workflow-definition.ts
|
|
317
317
|
init_dag_manager();
|
|
318
|
+
|
|
319
|
+
// src/env/workflow/utils/shell-dialect-lint.ts
|
|
320
|
+
var RULES = [
|
|
321
|
+
{
|
|
322
|
+
name: "pipefail",
|
|
323
|
+
re: /\bset\s+(?:-[A-Za-z]+\s+)*-?[A-Za-z]*o[A-Za-z]*\s+pipefail\b/,
|
|
324
|
+
fix: "dash 不支持 set -o pipefail(含 set -euo pipefail)。改用落文件再取退出码: " + "`cmd > /tmp/o 2>&1; RC=$?; cat /tmp/o; exit $RC`"
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
name: "PIPESTATUS",
|
|
328
|
+
re: /\$\{PIPESTATUS\[/,
|
|
329
|
+
fix: "dash 不支持 ${PIPESTATUS[n]}。改用把管道左端输出落文件,再单独取 $?: " + "`cmd > /tmp/o 2>&1; RC=$?`"
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: "substring-replace",
|
|
333
|
+
re: /\$\{[A-Za-z_][A-Za-z0-9_]*\/\//,
|
|
334
|
+
fix: "dash 不支持 ${VAR//a/b}。改用 tr 或 sed: " + '`V=$(echo "$V" | tr -d ".")`'
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
name: "BASH_SOURCE",
|
|
338
|
+
re: /\$\{BASH_SOURCE/,
|
|
339
|
+
fix: "dash 不支持 ${BASH_SOURCE[0]}。改用 $0(或显式写绝对路径)"
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: "process-substitution",
|
|
343
|
+
re: /(?:^|[\s;&|(])[<>]\([^)]/,
|
|
344
|
+
fix: "dash 不支持进程替换 <(cmd) / >(cmd)。改用显式 bash 或临时文件: " + "`bash -c 'diff <(a) <(b)'` 或 `a > /tmp/a; b > /tmp/b; diff /tmp/a /tmp/b`"
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
name: "here-string",
|
|
348
|
+
re: /(?<!<)<<<\s*[^\s<]/,
|
|
349
|
+
fix: "dash 不支持 here-string <<<。改用 printf 管道: " + '`printf "%s\\n" "$X" | cmd`'
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
name: "ansi-c-quote",
|
|
353
|
+
re: /\$'(?:[^'\\]|\\.)*\\/,
|
|
354
|
+
fix: "dash 不支持 ANSI-C 转义 $'...'(会静默保留反斜杠)。改用 printf: " + '`printf "a\\tb\\n"`'
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
name: "dbl-bracket",
|
|
358
|
+
re: /\[\[(?=[\s\-!$\u0027])/,
|
|
359
|
+
fix: 'dash 不支持 [[ ]]。改用 [ ](注意 quote 变量: `[ -f "$F" ]`)'
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
name: "source",
|
|
363
|
+
re: /(?:^|[\s;&|(])source\s+(?:~|\$|\.|\/)[^\s;|&]*|(?:^|[\s;&|(])source\s+[A-Za-z0-9_.-]+\.sh\b/,
|
|
364
|
+
fix: "dash 不支持 source。改用 POSIX 点命令: `. <file>`"
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
name: "array",
|
|
368
|
+
re: /(^|[\s;&|(])([A-Za-z_][A-Za-z0-9_]*)=\(/,
|
|
369
|
+
fix: "dash 不支持数组。改用空格分隔的字符串 + for 循环: " + '`PYS="3.12 3.13"; for p in $PYS; do ...; done`'
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
name: "function-keyword",
|
|
373
|
+
re: /(^|\n)[ \t]*function\s+[A-Za-z_]/,
|
|
374
|
+
fix: "dash 不支持 function 关键字。改用 `name() { ... }`"
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
name: "RANDOM",
|
|
378
|
+
re: /\$RANDOM\b/,
|
|
379
|
+
fix: "dash 不支持 $RANDOM(静默为空)。改用 " + "`awk -v s=$(date +%s%N) 'BEGIN{srand(s); print int(rand()*32768)}'`"
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
name: "echo-e",
|
|
383
|
+
re: /(^|[\s;&|(])echo\s+-[ne]*e[A-Za-z]*\b/,
|
|
384
|
+
fix: 'dash 的 echo 不解析 -e(会输出字面 "-e")。改用 printf: `printf "%s\\n" "$X"`'
|
|
385
|
+
}
|
|
386
|
+
];
|
|
387
|
+
function stripComments(command) {
|
|
388
|
+
const lines = command.split(`
|
|
389
|
+
`);
|
|
390
|
+
const out = [];
|
|
391
|
+
for (const line of lines) {
|
|
392
|
+
let inSingle = false;
|
|
393
|
+
let inDouble = false;
|
|
394
|
+
let cut = -1;
|
|
395
|
+
for (let i = 0;i < line.length; i++) {
|
|
396
|
+
const ch = line[i];
|
|
397
|
+
if (ch === "\\" && !inSingle) {
|
|
398
|
+
i++;
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
if (ch === "'" && !inDouble) {
|
|
402
|
+
inSingle = !inSingle;
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
if (ch === '"' && !inSingle) {
|
|
406
|
+
inDouble = !inDouble;
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
if (ch === "#" && !inSingle && !inDouble) {
|
|
410
|
+
const prev = i === 0 ? " " : line[i - 1];
|
|
411
|
+
if (/\s/.test(prev)) {
|
|
412
|
+
cut = i;
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
out.push(cut >= 0 ? line.slice(0, cut) : line);
|
|
418
|
+
}
|
|
419
|
+
return out.join(`
|
|
420
|
+
`);
|
|
421
|
+
}
|
|
422
|
+
function stripQuotedHeredocBodies(command) {
|
|
423
|
+
const lines = command.split(`
|
|
424
|
+
`);
|
|
425
|
+
const out = [];
|
|
426
|
+
let delim = null;
|
|
427
|
+
for (const line of lines) {
|
|
428
|
+
if (delim !== null) {
|
|
429
|
+
if (line.trim() === delim) {
|
|
430
|
+
out.push(line);
|
|
431
|
+
delim = null;
|
|
432
|
+
}
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
const m = /<<-?\s*(?:'([^']+)'|"([^"]+)")/.exec(line);
|
|
436
|
+
out.push(line);
|
|
437
|
+
if (m)
|
|
438
|
+
delim = (m[1] ?? m[2] ?? "").trim();
|
|
439
|
+
}
|
|
440
|
+
return out.join(`
|
|
441
|
+
`);
|
|
442
|
+
}
|
|
443
|
+
function stripForeignShellBodies(command) {
|
|
444
|
+
const re = /(\b(?:bash|sh)\s+-[a-z]*c[a-z]*\s+)(?:'[\s\S]*?'|"[\s\S]*?")/g;
|
|
445
|
+
return command.replace(re, "$1<foreign-shell-body>");
|
|
446
|
+
}
|
|
447
|
+
function isEntirelyBashInvocation(command) {
|
|
448
|
+
const re = /^(?:[A-Za-z_][A-Za-z0-9_]*=[^\s;|&]*\s+)*\/?(?:[\w./-]*\/)?bash\s+-(?:[a-z]*c[a-z]*)\s+(?:'[\s\S]*'|"[\s\S]*")\s*$/;
|
|
449
|
+
return re.test(command.trim());
|
|
450
|
+
}
|
|
451
|
+
function lintShellDialect(command) {
|
|
452
|
+
if (!command || typeof command !== "string")
|
|
453
|
+
return [];
|
|
454
|
+
const scannable = stripComments(stripForeignShellBodies(stripQuotedHeredocBodies(command)));
|
|
455
|
+
if (scannable.trim().length === 0)
|
|
456
|
+
return [];
|
|
457
|
+
if (isEntirelyBashInvocation(scannable))
|
|
458
|
+
return [];
|
|
459
|
+
const findings = [];
|
|
460
|
+
const seen = new Set;
|
|
461
|
+
for (const rule of RULES) {
|
|
462
|
+
if (seen.has(rule.name))
|
|
463
|
+
continue;
|
|
464
|
+
const m = rule.re.exec(scannable);
|
|
465
|
+
if (!m)
|
|
466
|
+
continue;
|
|
467
|
+
seen.add(rule.name);
|
|
468
|
+
const start = Math.max(0, m.index - 30);
|
|
469
|
+
const excerpt = scannable.slice(start, m.index + m[0].length + 40).replace(/\s+/g, " ").trim();
|
|
470
|
+
findings.push({ name: rule.name, fix: rule.fix, excerpt });
|
|
471
|
+
}
|
|
472
|
+
return findings;
|
|
473
|
+
}
|
|
474
|
+
function formatShellDialectFindings(nodeId, findings) {
|
|
475
|
+
return findings.map((f) => `[shell-dialect] node "${nodeId}" uses bash-only "${f.name}": ${f.fix} | near: ${f.excerpt}`);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// src/env/workflow/utils/validate-workflow-definition.ts
|
|
318
479
|
function validateWorkflowDefinition(definition, options = {}) {
|
|
319
480
|
if (!definition || typeof definition !== "object") {
|
|
320
481
|
return {
|
|
@@ -352,8 +513,17 @@ function validateWorkflowDefinition(definition, options = {}) {
|
|
|
352
513
|
const contractErrors = findings.filter((f) => f.severity === "error");
|
|
353
514
|
const contractWarnings = findings.filter((f) => f.severity === "warning");
|
|
354
515
|
const contractInfos = findings.filter((f) => f.severity === "info");
|
|
516
|
+
const dialectWarnings = [];
|
|
517
|
+
if (structural.valid && options.lintShellDialect !== false) {
|
|
518
|
+
try {
|
|
519
|
+
dialectWarnings.push(...collectShellDialectWarnings(definition));
|
|
520
|
+
} catch {}
|
|
521
|
+
}
|
|
355
522
|
const errors = [...structural.errors, ...formatTemplateContractFindings(contractErrors)];
|
|
356
|
-
const warnings =
|
|
523
|
+
const warnings = [
|
|
524
|
+
...formatTemplateContractFindings(contractWarnings),
|
|
525
|
+
...dialectWarnings
|
|
526
|
+
];
|
|
357
527
|
const infos = formatTemplateContractFindings(contractInfos);
|
|
358
528
|
return {
|
|
359
529
|
valid: structural.valid && contractErrors.length === 0,
|
|
@@ -363,5 +533,46 @@ function validateWorkflowDefinition(definition, options = {}) {
|
|
|
363
533
|
...findings.length > 0 ? { findings } : {}
|
|
364
534
|
};
|
|
365
535
|
}
|
|
536
|
+
function extractToolCommands(config) {
|
|
537
|
+
if (!config || typeof config !== "object")
|
|
538
|
+
return [];
|
|
539
|
+
const out = [];
|
|
540
|
+
const COMMAND_KEYS = ["command", "cmd"];
|
|
541
|
+
const CONTAINER_KEYS = ["input", "inputs", "args"];
|
|
542
|
+
for (const key of COMMAND_KEYS) {
|
|
543
|
+
const v = config[key];
|
|
544
|
+
if (typeof v === "string")
|
|
545
|
+
out.push(v);
|
|
546
|
+
}
|
|
547
|
+
for (const key of CONTAINER_KEYS) {
|
|
548
|
+
const container = config[key];
|
|
549
|
+
if (!container || typeof container !== "object" || Array.isArray(container))
|
|
550
|
+
continue;
|
|
551
|
+
for (const cmdKey of COMMAND_KEYS) {
|
|
552
|
+
const v = container[cmdKey];
|
|
553
|
+
if (typeof v === "string")
|
|
554
|
+
out.push(v);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
return out;
|
|
558
|
+
}
|
|
559
|
+
function collectShellDialectWarnings(definition) {
|
|
560
|
+
const def = definition;
|
|
561
|
+
if (!def || !Array.isArray(def.nodes))
|
|
562
|
+
return [];
|
|
563
|
+
const warnings = [];
|
|
564
|
+
for (const node of def.nodes) {
|
|
565
|
+
if (node?.type !== "tool")
|
|
566
|
+
continue;
|
|
567
|
+
const nodeId = typeof node.id === "string" ? node.id : String(node.id ?? "<unnamed>");
|
|
568
|
+
for (const command of extractToolCommands(node.config)) {
|
|
569
|
+
const findings = lintShellDialect(command);
|
|
570
|
+
if (findings.length > 0) {
|
|
571
|
+
warnings.push(...formatShellDialectFindings(nodeId, findings));
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return warnings;
|
|
576
|
+
}
|
|
366
577
|
|
|
367
578
|
export { VIRTUAL_OUTPUT_FIELD, NODE_OUTPUT_CONTRACTS, getNodeOutputContract, hasClosedOutputContract, RESULT_INVALID_NODE_TYPES, getAgentDeclaredJsonFields, validateTemplateContracts, getTemplateContractErrors, getTemplateContractWarnings, formatTemplateContractFindings, validateWorkflowDefinition };
|