akm-cli 0.10.261012-beta → 0.10.261013-beta
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/CHANGELOG.md +216 -0
- package/SECURITY.md +2 -2
- package/STABILITY.md +2 -2
- package/dist/assets/hints/cli-hints-full.md +6 -7
- package/dist/assets/hints/cli-hints-short.md +3 -4
- package/dist/commands/command/execution-source-loader.js +10 -14
- package/dist/commands/env/env-binding.js +47 -58
- package/dist/commands/env/env-cli.js +20 -22
- package/dist/commands/env/env.js +10 -58
- package/dist/commands/env/secret-cli.js +9 -27
- package/dist/commands/env/secret.js +3 -10
- package/dist/commands/health/scheduler-binary.js +1 -1
- package/dist/commands/improve/distill.js +0 -2
- package/dist/commands/improve/eligibility.js +0 -2
- package/dist/commands/lint/env-key-rules.js +7 -6
- package/dist/commands/lint/index.js +1 -1
- package/dist/commands/proposal/validators/proposal-validators.js +1 -4
- package/dist/commands/read/show.js +4 -25
- package/dist/commands/sources/add-cli.js +8 -63
- package/dist/commands/sources/bundle-cli.js +8 -9
- package/dist/commands/sources/installed-stashes.js +117 -61
- package/dist/commands/sources/schema-repair.js +0 -2
- package/dist/commands/sources/source-clone.js +31 -7
- package/dist/commands/tasks/explain.js +1 -1
- package/dist/commands/tasks/tasks.js +38 -56
- package/dist/commands/ui/redact.js +4 -4
- package/dist/commands/ui/workspace-actions.js +1 -1
- package/dist/commands/workflow/plan.js +47 -8
- package/dist/commands/workflow-cli.js +37 -41
- package/dist/core/adapter/adapters/akm-adapter.js +3 -3
- package/dist/core/adapter/adapters/akm-lint.js +3 -4
- package/dist/core/adapter/adapters/akm-metadata.js +2 -2
- package/dist/core/adapter/adapters/akm-task-adapter.js +5 -6
- package/dist/core/adapter/adapters/dotenv-adapter.js +4 -3
- package/dist/core/asset/resolve-ref.js +5 -0
- package/dist/core/bundle-rename.js +12 -4
- package/dist/core/common.js +4 -52
- package/dist/core/config/config-walker.js +1 -1
- package/dist/core/config/config.js +55 -18
- package/dist/core/config/schema/primitives.js +12 -6
- package/dist/core/config/schema/sources-bundles.js +2 -2
- package/dist/core/env-secret-ref.js +84 -73
- package/dist/core/errors.js +1 -1
- package/dist/core/paths.js +1 -6
- package/dist/core/redaction.js +16 -3
- package/dist/core/ref-token.js +82 -0
- package/dist/core/secret-value.js +14 -0
- package/dist/core/write-source.js +20 -4
- package/dist/indexer/indexer.js +27 -36
- package/dist/indexer/installations.js +1 -8
- package/dist/indexer/lookup/adapter-concept-owner.js +40 -14
- package/dist/indexer/materialize-embeddings.js +1 -1
- package/dist/indexer/scan/drain-dir.js +3 -3
- package/dist/indexer/search/search-source.js +4 -2
- package/dist/indexer/walk/path-resolver.js +13 -68
- package/dist/indexer/walk/walker.js +18 -22
- package/dist/integrations/agent/engine-resolution.js +9 -7
- package/dist/integrations/agent/runner-dispatch.js +4 -5
- package/dist/integrations/agent/runner.js +2 -0
- package/dist/llm/client.js +1 -1
- package/dist/llm/embedders/remote.js +4 -3
- package/dist/output/renderers.js +3 -4
- package/dist/output/shapes/helpers.js +0 -2
- package/dist/output/text/command-format.js +12 -2
- package/dist/output/text/show-directives.js +16 -28
- package/dist/output/text/workflow-format.js +37 -7
- package/dist/scripts/akm-migrate-node.js +45865 -44692
- package/dist/scripts/akm-migrate.js +45804 -44630
- package/dist/setup/steps/tasks.js +1 -2
- package/dist/sources/providers/provider-utils.js +12 -0
- package/dist/sources/resolve.js +12 -16
- package/dist/sources/snapshot-fetchers/secret-seam.js +2 -4
- package/dist/sources/snapshot-fetchers/x.js +1 -1
- package/dist/storage/repositories/proposals-repository.js +2 -2
- package/dist/storage/repositories/task-history-repository.js +2 -2
- package/dist/storage/repositories/workflow-runs-repository.js +38 -116
- package/dist/tasks/embedded.js +1 -1
- package/dist/tasks/prepare/prepare-support.js +3 -2
- package/dist/tasks/prepare/prepare.js +3 -4
- package/dist/tasks/prepare/script-capture.js +8 -22
- package/dist/tasks/run/load-task.js +46 -14
- package/dist/tasks/run/run-native-task.js +0 -8
- package/dist/tasks/run/run-workflow-task.js +2 -2
- package/dist/tasks/scheduler-sync.js +0 -1
- package/dist/tasks/source/bounded-document.js +0 -41
- package/dist/tasks/source/parse-task-source.js +0 -1
- package/dist/tasks/source/task-source-v4.js +1 -4
- package/dist/workflows/compile.js +1 -15
- package/dist/workflows/exec/child-workflow.js +1 -2
- package/dist/workflows/exec/dispatch-redaction.js +24 -6
- package/dist/workflows/exec/environment.js +11 -74
- package/dist/workflows/exec/exec-unit.js +31 -23
- package/dist/workflows/exec/frozen-judge.js +17 -3
- package/dist/workflows/exec/native-executor.js +70 -26
- package/dist/workflows/exec/run-workflow.js +31 -18
- package/dist/workflows/exec/step-work.js +4 -3
- package/dist/workflows/exec/unit-dispatch.js +1 -8
- package/dist/workflows/freeze/environment.js +14 -119
- package/dist/workflows/freeze/freeze.js +0 -1
- package/dist/workflows/freeze/step-values.js +1 -2
- package/dist/workflows/freeze/targets/child-workflow.js +3 -5
- package/dist/workflows/freeze/targets/command.js +6 -9
- package/dist/workflows/freeze/targets/shell.js +0 -3
- package/dist/workflows/parser.js +6 -25
- package/dist/workflows/plan.js +6 -2
- package/dist/workflows/renderer.js +77 -4
- package/dist/workflows/runtime/run-plan.js +37 -23
- package/dist/workflows/runtime/runs.js +41 -84
- package/dist/workflows/runtime/workflow-asset-loader.js +55 -86
- package/dist/workflows/source-files.js +7 -19
- package/docs/architecture/0.10-process-rules.md +6 -6
- package/docs/architecture/architecture.md +6 -6
- package/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md +12 -18
- package/docs/architecture/decisions/0003-child-env-allowlist-and-provenance.md +6 -8
- package/docs/architecture/internals/storage-locations.md +4 -4
- package/docs/architecture/persisted-data-compat.md +3 -2
- package/docs/architecture/specs/0.9.0-public-api-issue-backlog.md +1 -1
- package/docs/architecture/specs/akm-format-neutral-bundle-workspace-spec.md +3 -3
- package/docs/architecture/testing/manual-testing-checklist.md +46 -78
- package/docs/architecture/testing/task-workflow-manual-tests.md +22 -40
- package/docs/architecture/testing/testing-workflow.md +9 -11
- package/docs/architecture/workflow-engine.md +41 -27
- package/docs/guides/author-bundles.md +14 -17
- package/docs/guides/author-workflows.md +43 -9
- package/docs/guides/bundles.md +16 -11
- package/docs/guides/claude-code-vs-akm-workflows.md +1 -1
- package/docs/guides/run-workflows.md +25 -13
- package/docs/guides/scheduling.md +1 -1
- package/docs/maintainers/README.md +2 -3
- package/docs/maintainers/release-checklist.md +73 -126
- package/docs/migration/v0.9-to-v0.10.md +5 -1
- package/docs/reference/cli.md +124 -112
- package/docs/reference/configuration.md +16 -9
- package/docs/reference/data-and-telemetry.md +1 -1
- package/docs/reference/env-and-secrets.md +66 -15
- package/docs/reference/registry.md +6 -11
- package/docs/reference/tasks.md +11 -3
- package/docs/reference/ui-api.md +3 -3
- package/docs/reference/workflow-schema.md +109 -36
- package/package.json +1 -1
- package/schemas/akm-task.json +2 -4
- package/dist/commands/sources/dangerous-env-audit.js +0 -134
- package/dist/core/activation-policy.js +0 -67
- package/dist/execution/directory-identity.js +0 -78
- package/dist/workflows/authoring/scope-key.js +0 -79
- package/dist/workflows/source-semantics.js +0 -110
|
@@ -23,18 +23,21 @@ import { makeBundleRef, parseBundleRef } from "../../core/asset/asset-ref.js";
|
|
|
23
23
|
import { conceptIdFromTypeName, isFullRefInput } from "../../core/asset/resolve-ref.js";
|
|
24
24
|
import { isWithin, resolveStashDir } from "../../core/common.js";
|
|
25
25
|
import { loadConfig, mutateConfig, resetConfigCache } from "../../core/config/config.js";
|
|
26
|
-
import { bundleKeyForContentRoot,
|
|
26
|
+
import { bundleKeyForContentRoot, resolveConfiguredSources } from "../../core/config/config-sources.js";
|
|
27
27
|
import { IMPROVE_AUTONOMY_CONFIG_KEY, isImproveAutonomyEnabled } from "../../core/config/experimental.js";
|
|
28
28
|
import { ConfigError, NotFoundError, UsageError } from "../../core/errors.js";
|
|
29
29
|
import { getTaskHistoryDir, getTaskLogDir } from "../../core/paths.js";
|
|
30
30
|
import { warn } from "../../core/warn.js";
|
|
31
|
-
import { commitWriteTargetBoundary, isWriteCapableSourceKind, prepareWriteTargetForMutation, resolveWorkingStashTarget, resolveWriteTarget, writeAssetToSource, } from "../../core/write-source.js";
|
|
31
|
+
import { assertBundleNotDisabled, commitWriteTargetBoundary, isWriteCapableSourceKind, prepareWriteTargetForMutation, resolveWorkingStashTarget, resolveWriteTarget, writeAssetToSource, } from "../../core/write-source.js";
|
|
32
|
+
import { deriveInstallations } from "../../indexer/installations.js";
|
|
32
33
|
import { withEngineFallback } from "../../integrations/agent/engine-fallback.js";
|
|
34
|
+
import { resolveSourcesForOrigin } from "../../registry/origin-resolve.js";
|
|
33
35
|
import { resolveAssetPath } from "../../sources/resolve.js";
|
|
34
36
|
import { enabledRefsFromInstalled, schedulerEnabledRefs, setSchedulerRefEnabled } from "../../tasks/activation-config.js";
|
|
35
37
|
import { backendNameForPlatform, selectBackend } from "../../tasks/backends/index.js";
|
|
36
38
|
import { prepareTaskV3Execution } from "../../tasks/prepare/prepare.js";
|
|
37
39
|
import { isCheckoutInvocation, resolveAkmInvocation } from "../../tasks/resolve-akm-bin.js";
|
|
40
|
+
import { activeTaskSources, resolveBundleAsset } from "../../tasks/run/load-task.js";
|
|
38
41
|
import { createExecutionProvenanceContext } from "../../tasks/run/provenance.js";
|
|
39
42
|
import { runTask } from "../../tasks/run/run-task.js";
|
|
40
43
|
import { readTaskHistory } from "../../tasks/run/task-history.js";
|
|
@@ -78,7 +81,7 @@ export async function akmTasksAdd(input, deps = {}) {
|
|
|
78
81
|
when_to_use: input.when_to_use,
|
|
79
82
|
tags: input.tags,
|
|
80
83
|
});
|
|
81
|
-
const parsedTask = parseTaskSource({ yaml, filePath: assetPath
|
|
84
|
+
const parsedTask = parseTaskSource({ yaml, filePath: assetPath });
|
|
82
85
|
const task = projectTaskSourceV4(parsedTask.v4);
|
|
83
86
|
const qualifiedRef = makeBundleRef(bundle.bundleName, `tasks/${id}`);
|
|
84
87
|
await prepareTaskV3Execution(task, {
|
|
@@ -234,7 +237,6 @@ function resolveTaskActivation(ref, target, requireSchedulable) {
|
|
|
234
237
|
const parsedSource = parseTaskSource({
|
|
235
238
|
yaml: fs.readFileSync(sourcePath, "utf8"),
|
|
236
239
|
filePath: sourcePath,
|
|
237
|
-
workspaceRoot: bundle.source.path,
|
|
238
240
|
});
|
|
239
241
|
if (parsedSource.v4.schedule.length === 0) {
|
|
240
242
|
throw new UsageError(`Task ${JSON.stringify(ref)} has no schedule to enable.`, "INVALID_FLAG_VALUE");
|
|
@@ -361,24 +363,26 @@ async function buildSchedulerSyncPlan(deps, bundleTarget, options) {
|
|
|
361
363
|
if (options.dryRun !== true)
|
|
362
364
|
config = loadConfig();
|
|
363
365
|
}
|
|
364
|
-
|
|
366
|
+
// The one active-bundle set search, show and workflows walk, working stash included.
|
|
367
|
+
// A working stash that is a disabled configured bundle is left out: an
|
|
368
|
+
// unscoped sync still removes that bundle's rows (below) and syncs the others,
|
|
369
|
+
// and a scoped sync reports it as disabled only when it is the selected one.
|
|
370
|
+
const sources = activeTaskSources(config);
|
|
371
|
+
const ids = deriveInstallations(sources).map((installation) => installation.id);
|
|
372
|
+
const kinds = sources.map((source) => source.type);
|
|
373
|
+
const configuredSources = resolveConfiguredSources(config);
|
|
365
374
|
if (bundleTarget) {
|
|
366
375
|
// Only filesystem and git bundles can carry scheduler state.
|
|
367
|
-
const
|
|
368
|
-
if (
|
|
369
|
-
throw new UsageError(`Bundle "${bundleTarget}" has kind "${
|
|
376
|
+
const targetType = kinds[ids.indexOf(bundleTarget)];
|
|
377
|
+
if (targetType !== undefined && !isWriteCapableSourceKind(targetType)) {
|
|
378
|
+
throw new UsageError(`Bundle "${bundleTarget}" has kind "${targetType}"; task scheduling is only supported for filesystem and git bundles.`, "INVALID_FLAG_VALUE");
|
|
370
379
|
}
|
|
371
380
|
}
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
const selected = bundleTarget
|
|
378
|
-
? [bundleTarget]
|
|
379
|
-
: schedulable.length > 0 || configuredSources.length > 0
|
|
380
|
-
? schedulable
|
|
381
|
-
: [undefined];
|
|
381
|
+
const schedulable = ids.filter((_, index) => {
|
|
382
|
+
const type = kinds[index];
|
|
383
|
+
return type !== undefined && isWriteCapableSourceKind(type);
|
|
384
|
+
});
|
|
385
|
+
const selected = bundleTarget ? [bundleTarget] : schedulable;
|
|
382
386
|
const enabledRefSet = new Set(enabledRefs);
|
|
383
387
|
const desired = [];
|
|
384
388
|
const failures = [];
|
|
@@ -696,12 +700,7 @@ function taskProjectionAssetResolver(config, bundleName, bundleRoot) {
|
|
|
696
700
|
if (bundle === bundleName) {
|
|
697
701
|
return { file: await resolveAssetPath(bundleRoot, type, name), bundleRoot };
|
|
698
702
|
}
|
|
699
|
-
|
|
700
|
-
const target = resolveWriteTarget(config, bundle, { requireWritable: false, flag: "The asset ref's bundle" });
|
|
701
|
-
return {
|
|
702
|
-
file: await resolveAssetPath(target.source.path, type, name),
|
|
703
|
-
bundleRoot: target.source.path,
|
|
704
|
-
};
|
|
703
|
+
return resolveBundleAsset(config, { bundle, type, name });
|
|
705
704
|
};
|
|
706
705
|
}
|
|
707
706
|
/**
|
|
@@ -722,44 +721,27 @@ export function resolveTaskReadBundle(refBundle, flagBundle) {
|
|
|
722
721
|
resolved = resolveWorkingStashTarget(config, { requireWritable: false });
|
|
723
722
|
}
|
|
724
723
|
else {
|
|
725
|
-
|
|
726
|
-
const
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
724
|
+
// Reading needs no write target: any bundle of the active set resolves.
|
|
725
|
+
const sources = activeTaskSources(config);
|
|
726
|
+
const source = resolveSourcesForOrigin(selector, sources)[0];
|
|
727
|
+
if (!source) {
|
|
728
|
+
assertBundleNotDisabled(config, selector);
|
|
729
|
+
throw new UsageError(`${flagBundle !== undefined ? "--bundle" : "The task ref's bundle"} must reference a source name from your config. No source named "${selector}" is configured. Run \`akm bundle list\` to see available sources.`, "INVALID_FLAG_VALUE");
|
|
730
|
+
}
|
|
731
|
+
resolved = {
|
|
732
|
+
source: {
|
|
733
|
+
kind: source.type ?? "filesystem",
|
|
734
|
+
name: selector,
|
|
735
|
+
path: source.path,
|
|
736
|
+
adapterId: source.adapterId ?? detectAdapterId(source.path),
|
|
737
|
+
},
|
|
738
|
+
};
|
|
734
739
|
}
|
|
735
740
|
if (refBundle && resolved.source.name !== refBundle) {
|
|
736
741
|
throw new UsageError(`Task ref bundle ${JSON.stringify(refBundle)} does not match the resolved source.`, "INVALID_FLAG_VALUE");
|
|
737
742
|
}
|
|
738
743
|
return resolved;
|
|
739
744
|
}
|
|
740
|
-
/**
|
|
741
|
-
* New scheduler bindings always carry a canonical `--bundle <owner>` token,
|
|
742
|
-
* including an env-selected working stash. That stash need not be persisted in
|
|
743
|
-
* config (CI, one-shot tools, and fresh installs commonly use only
|
|
744
|
-
* AKM_BUNDLE_DIR), so its scheduled child — every row sets AKM_BUNDLE_DIR to
|
|
745
|
-
* the working stash it was synced from — must accept precisely its derived
|
|
746
|
-
* owner name.
|
|
747
|
-
*
|
|
748
|
-
* This is intentionally narrower than an unknown-bundle fallback: a configured
|
|
749
|
-
* source always wins, and an unconfigured selector is accepted only when it is
|
|
750
|
-
* exactly the current env-selected working stash identity.
|
|
751
|
-
*/
|
|
752
|
-
function resolveImplicitScheduledBundleTarget(config, selector) {
|
|
753
|
-
if (!process.env.AKM_BUNDLE_DIR?.trim())
|
|
754
|
-
return undefined;
|
|
755
|
-
try {
|
|
756
|
-
const working = resolveWorkingStashTarget(config, { requireWritable: false });
|
|
757
|
-
return working.source.name === selector ? working : undefined;
|
|
758
|
-
}
|
|
759
|
-
catch {
|
|
760
|
-
return undefined;
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
745
|
/** True when `candidate` resolves to the same directory as the primary stash. */
|
|
764
746
|
function isPrimaryStashPath(candidate) {
|
|
765
747
|
let primary;
|
|
@@ -7,15 +7,15 @@
|
|
|
7
7
|
* anything secret-like by key name, because the UI shows config the CLI's
|
|
8
8
|
* `config list` never had to defend against a browser leaking.
|
|
9
9
|
*
|
|
10
|
-
* Symbolic references (`$VAR`, `${VAR}`,
|
|
10
|
+
* Symbolic references (`$VAR`, `${VAR}`, `${secrets/name}`) are kept: they say
|
|
11
11
|
* where a credential comes from without being one.
|
|
12
12
|
*/
|
|
13
|
+
import { isApiKeyReference } from "../../core/config/schema/primitives.js";
|
|
13
14
|
const REDACTED = "[redacted]";
|
|
14
15
|
/** Key names whose string values are credentials, or sit in a bag of them. */
|
|
15
16
|
const SECRET_KEY = /(api[-_]?key|token|password|passwd|secret|credential|authorization|bearer|private[-_]?key)/i;
|
|
16
17
|
/** A whole subtree of values (`env`, `secrets`) that is data, not settings. */
|
|
17
18
|
const SECRET_BAG_KEY = /^(env|secrets?)$/i;
|
|
18
|
-
const SYMBOLIC_REFERENCE = /^(\$[A-Za-z_][A-Za-z0-9_]*|\$\{[A-Za-z_][A-Za-z0-9_]*\}|secret:\/\/.+)$/;
|
|
19
19
|
/** Query parameters that carry a credential in a URL. */
|
|
20
20
|
const SECRET_QUERY_PARAM = /^(api[-_]?key|key|token|access[-_]?token|auth|password|secret|sig|signature)$/i;
|
|
21
21
|
/** `scheme://user:pass@host/?token=x` with the credentials masked. Anything that is not a URL is returned as is. */
|
|
@@ -65,7 +65,7 @@ export function redactForUi(value, inSecretBag = false) {
|
|
|
65
65
|
if (Array.isArray(value))
|
|
66
66
|
return value.map((child) => redactForUi(child, inSecretBag));
|
|
67
67
|
if (typeof value === "string") {
|
|
68
|
-
if (
|
|
68
|
+
if (isApiKeyReference(value))
|
|
69
69
|
return value;
|
|
70
70
|
return inSecretBag ? REDACTED : redactUrlCredentials(value);
|
|
71
71
|
}
|
|
@@ -73,7 +73,7 @@ export function redactForUi(value, inSecretBag = false) {
|
|
|
73
73
|
return inSecretBag && value !== null ? REDACTED : value;
|
|
74
74
|
const result = {};
|
|
75
75
|
for (const [key, child] of Object.entries(value)) {
|
|
76
|
-
if (typeof child === "string" && SECRET_KEY.test(key) && !
|
|
76
|
+
if (typeof child === "string" && SECRET_KEY.test(key) && !isApiKeyReference(child)) {
|
|
77
77
|
result[key] = REDACTED;
|
|
78
78
|
}
|
|
79
79
|
else {
|
|
@@ -44,7 +44,7 @@ function positional(body, key) {
|
|
|
44
44
|
}
|
|
45
45
|
function preserveUnrelatedConfig(config = loadConfig()) {
|
|
46
46
|
if (!isDeepStrictEqual(sanitizeConfigForWrite(config, { quiet: true }), config)) {
|
|
47
|
-
throw new UsageError("AKM's config sanitizer would remove unrelated credentials. Move literal API keys to $VAR or
|
|
47
|
+
throw new UsageError("AKM's config sanitizer would remove unrelated credentials. Move literal API keys to $VAR or ${secrets/name} references and remove registry URL credentials using the CLI before changing bundle configuration in the UI.");
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
/** The error kind behind each exit code of the CLI contract (`EXIT_CODES`, `src/cli/shared.ts`). */
|
|
@@ -8,18 +8,22 @@
|
|
|
8
8
|
* (`loadWorkflowAsset`, `freezeWorkflow`) and NOTHING else — never
|
|
9
9
|
* `publishWorkflowRunV4`, `startWorkflowRun`, `appendEvent`, or `akmIndex`.
|
|
10
10
|
*
|
|
11
|
-
* SECRET-FREE, by construction (§4.6's closed print list): a
|
|
12
|
-
*
|
|
11
|
+
* SECRET-FREE, by construction (§4.6's closed print list): a step's
|
|
12
|
+
* `orchestration` is the authored argv, cwd and env names plus the
|
|
13
|
+
* bundle-qualified refs they resolve to (the projection `akm show` prints,
|
|
14
|
+
* never a value or a path); a resolved reference VALUE is never printed (references resolve at pre-attempt, not
|
|
13
15
|
* here); a `literal` **environment** binding's value is never printed (only
|
|
14
|
-
* `environment[].kind`/`.name`, and an `env-ref`'s `.ref
|
|
15
|
-
*
|
|
16
|
+
* `environment[].kind`/`.name`, and an `env-ref`'s `.ref` — all NAMES);
|
|
17
|
+
* `request.command.content`,
|
|
16
18
|
* `request.persona`, `request.conversation`, `request.runtime.environment`,
|
|
17
19
|
* and a script target's `bytesBase64` are never read at all.
|
|
18
20
|
*/
|
|
19
21
|
import { loadConfig } from "../../core/config/config.js";
|
|
22
|
+
import { NotFoundError } from "../../core/errors.js";
|
|
20
23
|
import { buildExecutionFromWire } from "../../integrations/agent/execution.js";
|
|
21
24
|
import { freezeWorkflow } from "../../workflows/freeze/freeze.js";
|
|
22
25
|
import { computePlanHash } from "../../workflows/ir/plan-hash.js";
|
|
26
|
+
import { resolveOrchestrationRefs, summarizeStepOrchestration } from "../../workflows/renderer.js";
|
|
23
27
|
import { loadWorkflowAsset } from "../../workflows/runtime/workflow-asset-loader.js";
|
|
24
28
|
/** The step's dispatch unit — the map template for a fan-out, else the root unit. Undefined for a step not yet frozen. */
|
|
25
29
|
function stepUnit(step) {
|
|
@@ -30,7 +34,7 @@ function stepUnit(step) {
|
|
|
30
34
|
}
|
|
31
35
|
function projectEnvironmentBinding(binding) {
|
|
32
36
|
if (binding.kind === "env-ref") {
|
|
33
|
-
return { kind: binding.kind, ref: binding.ref
|
|
37
|
+
return { kind: binding.kind, ref: binding.ref };
|
|
34
38
|
}
|
|
35
39
|
// literal / pass-through: kind + name only — a literal's VALUE is never printed.
|
|
36
40
|
return { kind: binding.kind, name: binding.name };
|
|
@@ -46,14 +50,23 @@ function childExpansion(target) {
|
|
|
46
50
|
via: "child",
|
|
47
51
|
childRef: target.ref,
|
|
48
52
|
childPlanHash: target.planHash,
|
|
49
|
-
steps: target.frozenPlan.steps.map((step, index) => projectStep(step, index)),
|
|
53
|
+
steps: target.frozenPlan.steps.map((step, index) => projectStep(step, index, stepOrchestration(step, target.frozenPlan))),
|
|
50
54
|
};
|
|
51
55
|
}
|
|
52
56
|
/** The child expansion boundary for one step (§4.6). */
|
|
53
57
|
function stepExpansion(frozenTarget) {
|
|
54
58
|
return frozenTarget?.kind === "child-workflow" ? childExpansion(frozenTarget) : { via: "direct" };
|
|
55
59
|
}
|
|
56
|
-
|
|
60
|
+
/**
|
|
61
|
+
* The step's `show` orchestration projection plus the refs it resolves to.
|
|
62
|
+
* `authored` is the compiled step (it keeps the spelling the author wrote);
|
|
63
|
+
* a child plan's steps have only their frozen form.
|
|
64
|
+
*/
|
|
65
|
+
function stepOrchestration(step, plan) {
|
|
66
|
+
const summary = summarizeStepOrchestration(step, plan.defaults);
|
|
67
|
+
return summary && resolveOrchestrationRefs(summary);
|
|
68
|
+
}
|
|
69
|
+
function projectStep(step, sequenceIndex, orchestration) {
|
|
57
70
|
const unit = stepUnit(step);
|
|
58
71
|
const frozenTarget = unit?.frozenTarget;
|
|
59
72
|
const kind = step.root?.kind === "map" ? "map" : "unit";
|
|
@@ -65,6 +78,7 @@ function projectStep(step, sequenceIndex) {
|
|
|
65
78
|
targetKind: frozenTarget?.kind ?? null,
|
|
66
79
|
...(step.root?.kind === "map" ? { concurrency: step.root.concurrency } : {}),
|
|
67
80
|
inputs: unit?.inputs ?? [],
|
|
81
|
+
...(orchestration ? { orchestration } : {}),
|
|
68
82
|
environment: (unit?.environment ?? []).map(projectEnvironmentBinding),
|
|
69
83
|
...(inputBindings && inputBindings.length > 0 ? { inputBindings: inputBindings.map(projectInputBinding) } : {}),
|
|
70
84
|
gate: {
|
|
@@ -102,15 +116,40 @@ function collectLoweringNotices(plan, config) {
|
|
|
102
116
|
}
|
|
103
117
|
return notices;
|
|
104
118
|
}
|
|
119
|
+
/** Every ref the steps' orchestration names that no bundle owns, as `ref (where of step "id")`. */
|
|
120
|
+
function unresolvedRefs(steps, orchestrations) {
|
|
121
|
+
return orchestrations.flatMap((orchestration, index) => (orchestration?.unresolved ?? []).map((missing) => `${missing.ref} (${missing.in} of step "${steps[index]?.stepId}")`));
|
|
122
|
+
}
|
|
123
|
+
/** The unresolved refs of every embedded child plan, recursively, each prefixed with the child workflow it is in. */
|
|
124
|
+
function unresolvedInChildren(plan) {
|
|
125
|
+
return plan.steps.flatMap((step) => {
|
|
126
|
+
const target = stepUnit(step)?.frozenTarget;
|
|
127
|
+
if (target?.kind !== "child-workflow")
|
|
128
|
+
return [];
|
|
129
|
+
const child = target.frozenPlan;
|
|
130
|
+
const own = unresolvedRefs(child.steps, child.steps.map((childStep) => stepOrchestration(childStep, child)));
|
|
131
|
+
return [...own.map((entry) => `${entry} in child ${target.ref}`), ...unresolvedInChildren(child)];
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function failOnUnresolved(ref, unresolved) {
|
|
135
|
+
if (unresolved.length === 0)
|
|
136
|
+
return;
|
|
137
|
+
throw new NotFoundError(`Workflow ${ref} names asset(s) that no bundle owns: ${unresolved.join(", ")}.`, "FILE_NOT_FOUND");
|
|
138
|
+
}
|
|
105
139
|
/**
|
|
106
140
|
* Compile + freeze `ref` and project the frozen plan into the read-only
|
|
107
141
|
* `akm workflow plan` envelope. Never publishes, never writes, never warns.
|
|
108
142
|
*/
|
|
109
143
|
export async function akmWorkflowPlan(ref) {
|
|
110
144
|
const asset = await loadWorkflowAsset(ref);
|
|
145
|
+
// Resolve what the authored steps name before freezing: a `${ref}`, env ref or child workflow no bundle owns fails the plan.
|
|
146
|
+
const orchestrations = asset.plan.steps.map((step) => stepOrchestration(step, asset.plan));
|
|
147
|
+
failOnUnresolved(asset.ref, unresolvedRefs(asset.plan.steps, orchestrations));
|
|
111
148
|
const config = loadConfig();
|
|
112
149
|
const frozen = await freezeWorkflow(asset, config);
|
|
113
150
|
const plan = frozen.plan;
|
|
151
|
+
// A child workflow's steps exist only once frozen; they fail the plan exactly as the top level's do.
|
|
152
|
+
failOnUnresolved(asset.ref, unresolvedInChildren(plan));
|
|
114
153
|
return {
|
|
115
154
|
ok: true,
|
|
116
155
|
ref: asset.ref,
|
|
@@ -123,7 +162,7 @@ export async function akmWorkflowPlan(ref) {
|
|
|
123
162
|
execution: plan.execution,
|
|
124
163
|
...(plan.budget ? { budget: plan.budget } : {}),
|
|
125
164
|
...(plan.params ? { params: plan.params } : {}),
|
|
126
|
-
steps: plan.steps.map((step, index) => projectStep(step, index)),
|
|
165
|
+
steps: plan.steps.map((step, index) => projectStep(step, index, orchestrations[index])),
|
|
127
166
|
notices: collectLoweringNotices(plan, config),
|
|
128
167
|
warnings: frozen.warnings.map((warning) => warning.message),
|
|
129
168
|
};
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
/**
|
|
5
|
-
* `akm workflow` command family. `run` is the canonical start/
|
|
6
|
-
*
|
|
5
|
+
* `akm workflow` command family. `run` is the canonical start/execute surface
|
|
6
|
+
* (a ref starts a new run, a run id drives that run) and `resume` reopens a
|
|
7
|
+
* run; the former public `start`, `next`, and `complete` lifecycle is gone.
|
|
7
8
|
* `create --print` emits Markdown, the only workflow source format. Validate
|
|
8
9
|
* with `akm lint --type workflows`.
|
|
9
10
|
*/
|
|
@@ -13,7 +14,6 @@ import { armAbortDeadline } from "../core/abort-deadline.js";
|
|
|
13
14
|
import { assertFlatAssetName, combineCreatePath, normalizeCreateSubPath } from "../core/asset/asset-create.js";
|
|
14
15
|
import { NotFoundError, TransientError, UsageError } from "../core/errors.js";
|
|
15
16
|
import { warn } from "../core/warn.js";
|
|
16
|
-
import { akmIndex } from "../indexer/indexer.js";
|
|
17
17
|
import { assertWorkflowMarkdownName, createWorkflowAsset, getWorkflowTemplate } from "../workflows/authoring/authoring.js";
|
|
18
18
|
import { WORKFLOW_MAX_TIMEOUT_MS } from "../workflows/resource-limits.js";
|
|
19
19
|
import { abandonWorkflowRun, getWorkflowStatus, listWorkflowRuns, resolveWorkflowRunTarget, resumeWorkflowRun, } from "../workflows/runtime/runs.js";
|
|
@@ -21,7 +21,7 @@ import { akmWorkflowPlan } from "./workflow/plan.js";
|
|
|
21
21
|
const workflowStatusCommand = defineJsonCommand({
|
|
22
22
|
meta: {
|
|
23
23
|
name: "status",
|
|
24
|
-
description: "Show full workflow run state for review or resume; workflow
|
|
24
|
+
description: "Show full workflow run state for review or resume; a workflow ref shows its most recent run",
|
|
25
25
|
},
|
|
26
26
|
args: {
|
|
27
27
|
target: { type: "positional", description: "Workflow run id or workflow ref (workflows/<name>)", required: true },
|
|
@@ -31,16 +31,17 @@ const workflowStatusCommand = defineJsonCommand({
|
|
|
31
31
|
"diagnostic text). Diagnostics only — step evidence stays deterministic and is unaffected.",
|
|
32
32
|
default: false,
|
|
33
33
|
},
|
|
34
|
+
// Deprecated no-op, accepted through 0.10 so scripts that pass it do not
|
|
35
|
+
// fail with UNKNOWN_FLAG. Runs are no longer scoped to a directory.
|
|
34
36
|
"all-scopes": {
|
|
35
37
|
type: "boolean",
|
|
36
|
-
description: "
|
|
38
|
+
description: "Deprecated, no effect: runs are host-wide, not scoped to a directory. Accepted through 0.10 for existing scripts.",
|
|
37
39
|
default: false,
|
|
38
40
|
},
|
|
39
41
|
},
|
|
40
42
|
async run({ args }) {
|
|
41
43
|
const target = args.target;
|
|
42
44
|
const includeUnits = args.units === true;
|
|
43
|
-
const allScopes = args["all-scopes"] === true;
|
|
44
45
|
const resolvedRunId = await resolveWorkflowRunTarget(target);
|
|
45
46
|
if (resolvedRunId !== undefined) {
|
|
46
47
|
const result = await getWorkflowStatus(resolvedRunId, { includeUnits });
|
|
@@ -48,9 +49,8 @@ const workflowStatusCommand = defineJsonCommand({
|
|
|
48
49
|
return;
|
|
49
50
|
}
|
|
50
51
|
let runs;
|
|
51
|
-
let scopeKey;
|
|
52
52
|
try {
|
|
53
|
-
({ runs
|
|
53
|
+
({ runs } = await listWorkflowRuns({ workflowRef: target }));
|
|
54
54
|
}
|
|
55
55
|
catch (error) {
|
|
56
56
|
if (!target.includes(":") && !target.includes("/")) {
|
|
@@ -60,14 +60,6 @@ const workflowStatusCommand = defineJsonCommand({
|
|
|
60
60
|
}
|
|
61
61
|
const mostRecent = runs[0];
|
|
62
62
|
if (!mostRecent) {
|
|
63
|
-
// #942: name the scope actually searched and point at `--all-scopes`
|
|
64
|
-
// rather than a bare "not found" — the ref-fallthrough lookup is
|
|
65
|
-
// scope-local by default, so "no runs" here means "none in THIS
|
|
66
|
-
// scope", not "none anywhere". Already searching every scope (or no
|
|
67
|
-
// real scope was filtered on) has nothing more specific to suggest.
|
|
68
|
-
if (!allScopes && scopeKey !== null) {
|
|
69
|
-
throw new NotFoundError(`No workflow runs found for ${target} in scope ${scopeKey}.`, "WORKFLOW_NOT_FOUND", `Run 'akm workflow status ${target} --all-scopes' to search every scope.`);
|
|
70
|
-
}
|
|
71
63
|
throw new NotFoundError(`No workflow runs found for ${target}`, "WORKFLOW_NOT_FOUND");
|
|
72
64
|
}
|
|
73
65
|
const result = await getWorkflowStatus(mostRecent.id, { includeUnits });
|
|
@@ -77,9 +69,7 @@ const workflowStatusCommand = defineJsonCommand({
|
|
|
77
69
|
const workflowListCommand = defineJsonCommand({
|
|
78
70
|
meta: {
|
|
79
71
|
name: "list",
|
|
80
|
-
description: "List workflow runs
|
|
81
|
-
"sit in another scope: get the run id from `akm task history <task-id> --limit 1` (detail.runId) and pass it " +
|
|
82
|
-
"to `akm workflow status <run-id>`, which works from any directory, or pass --all-scopes.",
|
|
72
|
+
description: "List workflow runs on this host, most recently updated first",
|
|
83
73
|
},
|
|
84
74
|
args: {
|
|
85
75
|
ref: { type: "string", description: "Filter to one workflow ref" },
|
|
@@ -89,11 +79,11 @@ const workflowListCommand = defineJsonCommand({
|
|
|
89
79
|
description: "Also include child workflow runs (hidden by default, P3b)",
|
|
90
80
|
default: false,
|
|
91
81
|
},
|
|
82
|
+
// Deprecated no-op, accepted through 0.10 so scripts that pass it do not
|
|
83
|
+
// fail with UNKNOWN_FLAG. Runs are no longer scoped to a directory.
|
|
92
84
|
"all-scopes": {
|
|
93
85
|
type: "boolean",
|
|
94
|
-
description: "
|
|
95
|
-
"`null` with this flag, otherwise the scope that was searched — so an empty `runs: []` is never " +
|
|
96
|
-
'indistinguishable from "nothing anywhere".',
|
|
86
|
+
description: "Deprecated, no effect: runs are host-wide, not scoped to a directory. Accepted through 0.10 for existing scripts.",
|
|
97
87
|
default: false,
|
|
98
88
|
},
|
|
99
89
|
},
|
|
@@ -102,7 +92,6 @@ const workflowListCommand = defineJsonCommand({
|
|
|
102
92
|
workflowRef: args.ref,
|
|
103
93
|
activeOnly: args.active,
|
|
104
94
|
includeChildren: args.children,
|
|
105
|
-
allScopes: args["all-scopes"],
|
|
106
95
|
});
|
|
107
96
|
output("workflow-list", result);
|
|
108
97
|
},
|
|
@@ -158,12 +147,6 @@ const workflowCreateCommand = defineJsonCommand({
|
|
|
158
147
|
from: args.from,
|
|
159
148
|
force: args.force,
|
|
160
149
|
});
|
|
161
|
-
// Index the newly-written workflow so `akm workflow run` can resolve
|
|
162
|
-
// a workflowEntryId without requiring an explicit `akm index` call
|
|
163
|
-
// first. Uses the same incremental index path that `akm add` uses.
|
|
164
|
-
// `result.bundleDir` — the indexer's own `IndexOptions.stashDir` field
|
|
165
|
-
// name is unchanged (indexer vocabulary is out of scope, P4 row B-50).
|
|
166
|
-
await akmIndex({ stashDir: result.bundleDir });
|
|
167
150
|
output("workflow-create", { ok: true, ...result });
|
|
168
151
|
},
|
|
169
152
|
});
|
|
@@ -184,29 +167,34 @@ const WORKFLOW_RUN_SKIP_REASONS = {
|
|
|
184
167
|
const workflowRunCommand = defineJsonCommand({
|
|
185
168
|
meta: {
|
|
186
169
|
name: "run",
|
|
187
|
-
description: "Start
|
|
170
|
+
description: "Start a new run of a workflow ref, or drive an existing run by id, through completion, failure, a verification gate, or an explicit limit",
|
|
188
171
|
},
|
|
189
172
|
args: {
|
|
190
|
-
target: {
|
|
173
|
+
target: {
|
|
174
|
+
type: "positional",
|
|
175
|
+
description: "Workflow ref (starts a new run) or workflow run id (drives that run)",
|
|
176
|
+
required: true,
|
|
177
|
+
},
|
|
191
178
|
"max-steps": { type: "string", description: "Stop after executing this many steps" },
|
|
192
179
|
"max-retries": { type: "string", description: "Retry a failed workflow step this many additional times" },
|
|
193
180
|
timeout: { type: "string", description: "Whole-run timeout: N, Nms, Ns, or Nm (bare N is milliseconds)" },
|
|
181
|
+
// Deprecated no-op, accepted through 0.10 so scripts that pass it do not
|
|
182
|
+
// fail with UNKNOWN_FLAG. A workflow ref always starts a new run now.
|
|
194
183
|
new: {
|
|
195
184
|
type: "boolean",
|
|
196
|
-
description: "
|
|
197
|
-
"(never abandons it). A workflow ref only — passing a run id with --new is a usage error.",
|
|
185
|
+
description: "Deprecated, no effect: a workflow ref always starts a new run. Accepted through 0.10 for existing scripts.",
|
|
198
186
|
default: false,
|
|
199
187
|
},
|
|
200
188
|
"skip-if-locked": {
|
|
201
189
|
type: "boolean",
|
|
202
|
-
description: "If another akm process is already driving this run
|
|
203
|
-
"writer, skip gracefully (exit 0) instead of failing (exit 75). Use for
|
|
204
|
-
"so they don't pile up failures while a longer-running invocation is in progress.",
|
|
190
|
+
description: "If another akm process is already driving this run (or, for a workflow ref, an active run of it), or " +
|
|
191
|
+
"state.db is busy with another writer, skip gracefully (exit 0) instead of failing (exit 75). Use for " +
|
|
192
|
+
"high-frequency scheduled runs so they don't pile up failures while a longer-running invocation is in progress.",
|
|
205
193
|
default: false,
|
|
206
194
|
},
|
|
207
195
|
},
|
|
208
196
|
async run({ args, rawArgs }) {
|
|
209
|
-
const { runWorkflowSteps } = await import("../workflows/exec/run-workflow.js");
|
|
197
|
+
const { findLiveLockedRunOfRef, runWorkflowSteps } = await import("../workflows/exec/run-workflow.js");
|
|
210
198
|
const parameterFlags = parseWorkflowParameterFlags(rawArgs, args.target);
|
|
211
199
|
const maxSteps = parseIntegerFlag(getStringArg(args, "max-steps"), "--max-steps", 1);
|
|
212
200
|
const maxRetries = parseIntegerFlag(getStringArg(args, "max-retries"), "--max-retries", 0);
|
|
@@ -228,15 +216,25 @@ const workflowRunCommand = defineJsonCommand({
|
|
|
228
216
|
timeoutMs,
|
|
229
217
|
reason: `Workflow run timed out after ${timeoutMs}ms.`,
|
|
230
218
|
});
|
|
219
|
+
const skip = (reason, message) => {
|
|
220
|
+
warn(`[workflow] ${message} skipping (--skip-if-locked)`);
|
|
221
|
+
output("workflow-run", { ok: true, target: args.target, skipped: { reason, message } });
|
|
222
|
+
};
|
|
231
223
|
try {
|
|
232
224
|
let result;
|
|
233
225
|
try {
|
|
226
|
+
// #1239: a ref start never conflicts with another run of the ref, so
|
|
227
|
+
// the one extra condition is an active run of it a live process is driving.
|
|
228
|
+
const held = skipIfLocked ? await findLiveLockedRunOfRef(args.target) : undefined;
|
|
229
|
+
if (held) {
|
|
230
|
+
skip("lock-held", `Workflow run ${held.runId} of ${args.target} is already being driven by another akm process (pid ${held.holder}).`);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
234
233
|
result = await runWorkflowSteps({
|
|
235
234
|
target: args.target,
|
|
236
235
|
parameterFlags,
|
|
237
236
|
...(maxSteps !== undefined ? { maxSteps } : {}),
|
|
238
237
|
...(maxRetries !== undefined ? { maxRetries } : {}),
|
|
239
|
-
newRun: args.new,
|
|
240
238
|
signal: controller.signal,
|
|
241
239
|
});
|
|
242
240
|
}
|
|
@@ -247,9 +245,7 @@ const workflowRunCommand = defineJsonCommand({
|
|
|
247
245
|
// off UsageError) — a bad flag or malformed input still fails loudly
|
|
248
246
|
// even with the flag set.
|
|
249
247
|
if (skipIfLocked && err instanceof TransientError && WORKFLOW_RUN_SKIP_REASONS[err.code]) {
|
|
250
|
-
|
|
251
|
-
warn(`[workflow] ${err.message} skipping (--skip-if-locked)`);
|
|
252
|
-
output("workflow-run", { ok: true, target: args.target, skipped: { reason, message: err.message } });
|
|
248
|
+
skip(WORKFLOW_RUN_SKIP_REASONS[err.code], err.message);
|
|
253
249
|
return;
|
|
254
250
|
}
|
|
255
251
|
throw err;
|
|
@@ -83,6 +83,7 @@ import path from "node:path";
|
|
|
83
83
|
import { applyPostContributorFields, applyPreContributorFields, extractPackageMetadata, getMarkdownFragmentContent, hasMarkdownFragmentContent, setMarkdownFragmentContent, } from "../../../indexer/passes/metadata.js";
|
|
84
84
|
import { assetPathCandidatesForName, deriveCanonicalAssetNameFromStashRoot, placementTypes, stashDirFor, stashDirNames, } from "../../asset/asset-placement.js";
|
|
85
85
|
import { parseFrontmatter } from "../../asset/frontmatter.js";
|
|
86
|
+
import { sensitiveMarkerPath } from "../../sensitive-marker-path.js";
|
|
86
87
|
import { executionDefaultsFromFrontmatter, renderMarkdownExecutionSource } from "../execution-source.js";
|
|
87
88
|
import { recognizeMatch } from "../recognize-match.js";
|
|
88
89
|
import { perTypeValidateChecks, skillDirectoryDiagnostics } from "./akm-lint.js";
|
|
@@ -136,7 +137,7 @@ function akmStashAbstains(root, absPath) {
|
|
|
136
137
|
return true;
|
|
137
138
|
// Skip env `.env` files that have a sibling `.sensitive` marker file.
|
|
138
139
|
if (segments[0] === "env" && (absPath.endsWith(".env") || path.basename(absPath) === ".env")) {
|
|
139
|
-
if (fs.existsSync(absPath
|
|
140
|
+
if (fs.existsSync(sensitiveMarkerPath(absPath, "env")))
|
|
140
141
|
return true;
|
|
141
142
|
}
|
|
142
143
|
// Skip secret files that are themselves a `.sensitive` marker, or that have a
|
|
@@ -145,7 +146,7 @@ function akmStashAbstains(root, absPath) {
|
|
|
145
146
|
if (segments[0] === "secrets") {
|
|
146
147
|
if (absPath.endsWith(".sensitive") || absPath.endsWith(".lock"))
|
|
147
148
|
return true;
|
|
148
|
-
if (fs.existsSync(
|
|
149
|
+
if (fs.existsSync(sensitiveMarkerPath(absPath, "secret")))
|
|
149
150
|
return true;
|
|
150
151
|
}
|
|
151
152
|
// Inside a `wikis/<name>/` directory, the root-level infrastructure files
|
|
@@ -439,7 +440,6 @@ async function validate(c, changes, ctx) {
|
|
|
439
440
|
body: parsed.content,
|
|
440
441
|
ext: overlay.ext,
|
|
441
442
|
ctx,
|
|
442
|
-
workspaceRoot: c.root,
|
|
443
443
|
})));
|
|
444
444
|
diagnostics.push(...(await skillDirectoryDiagnostics(change.path, seenSkillDirs, ctx)));
|
|
445
445
|
}
|
|
@@ -207,7 +207,7 @@ export async function skillDirectoryDiagnostics(relPath, seen, ctx, skillDirs =
|
|
|
207
207
|
* routes through `ctx`.
|
|
208
208
|
*/
|
|
209
209
|
export async function perTypeValidateChecks(args) {
|
|
210
|
-
const { type, relPath, raw, data, frontmatter, body, ext, ctx
|
|
210
|
+
const { type, relPath, raw, data, frontmatter, body, ext, ctx } = args;
|
|
211
211
|
switch (type) {
|
|
212
212
|
case "command":
|
|
213
213
|
return nameOrTypeDiagnostics(relPath, data, frontmatter, ["command"]);
|
|
@@ -216,7 +216,7 @@ export async function perTypeValidateChecks(args) {
|
|
|
216
216
|
case "fact":
|
|
217
217
|
return factDiagnostics(relPath, data);
|
|
218
218
|
case "task":
|
|
219
|
-
return taskDiagnostics(relPath, raw
|
|
219
|
+
return taskDiagnostics(relPath, raw);
|
|
220
220
|
case "workflow":
|
|
221
221
|
return ext === ".md" ? workflowDiagnostics(relPath, raw, body) : [];
|
|
222
222
|
case "memory":
|
|
@@ -266,12 +266,11 @@ export function factDiagnostics(relPath, data) {
|
|
|
266
266
|
* contract; the cron suits the local scheduler backend), so `akm lint --type
|
|
267
267
|
* tasks` never reports clean a task that sync would refuse.
|
|
268
268
|
*/
|
|
269
|
-
export function taskDiagnostics(relPath, raw
|
|
269
|
+
export function taskDiagnostics(relPath, raw) {
|
|
270
270
|
try {
|
|
271
271
|
const parsed = parseTaskSource({
|
|
272
272
|
filePath: relPath,
|
|
273
273
|
yaml: raw,
|
|
274
|
-
...(workspaceRoot ? { workspaceRoot } : {}),
|
|
275
274
|
});
|
|
276
275
|
assertTaskScheduleInputsSatisfyContract(parsed.v4, relPath);
|
|
277
276
|
assertTaskScheduleCronValid(parsed.v4, backendNameForPlatform());
|
|
@@ -170,7 +170,7 @@ export function foldRecognizedMetadata(rendererName, file) {
|
|
|
170
170
|
case "task-yaml": {
|
|
171
171
|
out.tags = Array.from(new Set([...(out.tags ?? []), "task", "scheduled"]));
|
|
172
172
|
try {
|
|
173
|
-
const parsed = parseTaskSource({ yaml: file.content(), filePath: file.absPath
|
|
173
|
+
const parsed = parseTaskSource({ yaml: file.content(), filePath: file.absPath });
|
|
174
174
|
const v4 = parsed.v4;
|
|
175
175
|
const target = v4.target;
|
|
176
176
|
const hints = new Set();
|
|
@@ -261,7 +261,7 @@ export function foldRecognizedMetadata(rendererName, file) {
|
|
|
261
261
|
// ── workflow-document-metadata (historical renderer id `workflow-md`, peer sources) ──
|
|
262
262
|
case "workflow-md": {
|
|
263
263
|
try {
|
|
264
|
-
const result = compileWorkflowSource(file.content(), { path: file.relPath
|
|
264
|
+
const result = compileWorkflowSource(file.content(), { path: file.relPath });
|
|
265
265
|
if (!result.ok)
|
|
266
266
|
return out;
|
|
267
267
|
const plan = result.plan;
|