@orchagent/cli 0.3.118 → 0.3.120
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/AGENT_GUIDE.md +34 -0
- package/dist/commands/agents.js +15 -2
- package/dist/commands/context.js +76 -0
- package/dist/commands/describe.js +131 -0
- package/dist/commands/index.js +6 -0
- package/dist/commands/info.js +8 -2
- package/dist/commands/login.js +18 -6
- package/dist/commands/logs.js +35 -1
- package/dist/commands/publish.js +107 -12
- package/dist/commands/run.js +39 -9
- package/dist/commands/schedule.js +37 -5
- package/dist/commands/schema.js +78 -0
- package/dist/commands/secrets.js +17 -2
- package/dist/commands/service.js +20 -3
- package/dist/commands/storage.js +61 -24
- package/dist/index.js +21 -2
- package/dist/lib/agent-ref.js +3 -0
- package/dist/lib/command-introspection.js +250 -0
- package/dist/lib/errors.js +125 -18
- package/dist/lib/list-options.js +69 -0
- package/dist/lib/llm.js +35 -0
- package/dist/lib/output.js +29 -0
- package/dist/lib/sanitize.js +99 -0
- package/dist/lib/validate.js +17 -0
- package/package.json +3 -2
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Input hardening utilities (DX-29).
|
|
4
|
+
*
|
|
5
|
+
* Central sanitization for:
|
|
6
|
+
* 4a — Control character rejection
|
|
7
|
+
* 4b — Path traversal bounds checking
|
|
8
|
+
* 4c — Resource ID validation (no query-param chars, no double-encoding)
|
|
9
|
+
*/
|
|
10
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.rejectControlChars = rejectControlChars;
|
|
15
|
+
exports.rejectResourceIdChars = rejectResourceIdChars;
|
|
16
|
+
exports.ensurePathInBounds = ensurePathInBounds;
|
|
17
|
+
exports.rejectPathTraversal = rejectPathTraversal;
|
|
18
|
+
exports.sanitizeSecretValue = sanitizeSecretValue;
|
|
19
|
+
const path_1 = __importDefault(require("path"));
|
|
20
|
+
const errors_1 = require("./errors");
|
|
21
|
+
// ASCII control chars (0x00-0x1F) minus allowed whitespace (\t=0x09, \n=0x0A, \r=0x0D).
|
|
22
|
+
// Also includes DEL (0x7F).
|
|
23
|
+
// eslint-disable-next-line no-control-regex
|
|
24
|
+
const DANGEROUS_CONTROL_RE = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/;
|
|
25
|
+
// Characters that must never appear in resource identifiers (agent names, org names, versions).
|
|
26
|
+
// These could inject query params, fragments, or URL-encoded path traversals.
|
|
27
|
+
const RESOURCE_ID_UNSAFE_RE = /[?#%&=]/;
|
|
28
|
+
/**
|
|
29
|
+
* Reject ASCII control characters in a string (except \n, \r, \t).
|
|
30
|
+
* Throws CliError with INVALID_INPUT if found.
|
|
31
|
+
*/
|
|
32
|
+
function rejectControlChars(value, fieldName) {
|
|
33
|
+
if (DANGEROUS_CONTROL_RE.test(value)) {
|
|
34
|
+
const err = new errors_1.CliError(`${fieldName} contains an invalid control character. ` +
|
|
35
|
+
'Remove non-printable characters and try again.', errors_1.ExitCodes.INVALID_INPUT);
|
|
36
|
+
err.code = errors_1.ErrorCodes.INVALID_INPUT;
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Reject characters that could inject query params, fragments, or URL encoding
|
|
42
|
+
* into resource identifiers (agent names, org names, version strings).
|
|
43
|
+
*
|
|
44
|
+
* Blocked: ? # % & =
|
|
45
|
+
* Also delegates to rejectControlChars.
|
|
46
|
+
*/
|
|
47
|
+
function rejectResourceIdChars(value, fieldName) {
|
|
48
|
+
rejectControlChars(value, fieldName);
|
|
49
|
+
const match = value.match(RESOURCE_ID_UNSAFE_RE);
|
|
50
|
+
if (match) {
|
|
51
|
+
const err = new errors_1.CliError(`${fieldName} contains '${match[0]}' which is not allowed. ` +
|
|
52
|
+
'Resource identifiers must not contain ?, #, %, &, or = characters.', errors_1.ExitCodes.INVALID_INPUT);
|
|
53
|
+
err.code = errors_1.ErrorCodes.INVALID_INPUT;
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Verify that a resolved (absolute) path stays within an allowed base directory.
|
|
59
|
+
* Both paths must be absolute. The resolved path is checked after normalization.
|
|
60
|
+
*/
|
|
61
|
+
function ensurePathInBounds(resolvedPath, basePath) {
|
|
62
|
+
const normalizedBase = path_1.default.resolve(basePath) + path_1.default.sep;
|
|
63
|
+
const normalizedTarget = path_1.default.resolve(resolvedPath);
|
|
64
|
+
// Allow exact match (target IS the base) or target is inside base
|
|
65
|
+
if (normalizedTarget !== path_1.default.resolve(basePath) && !normalizedTarget.startsWith(normalizedBase)) {
|
|
66
|
+
const err = new errors_1.CliError(`Path '${resolvedPath}' is outside the allowed directory '${basePath}'. ` +
|
|
67
|
+
'File references must not escape the project root.', errors_1.ExitCodes.INVALID_INPUT);
|
|
68
|
+
err.code = errors_1.ErrorCodes.INVALID_INPUT;
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Reject path traversal sequences in a raw file path argument.
|
|
74
|
+
*
|
|
75
|
+
* Blocks `../` (and `..\` on Windows) in the raw input string. Absolute paths
|
|
76
|
+
* are allowed — the user explicitly chose them. This catches accidental or
|
|
77
|
+
* malicious relative traversal while still permitting `/home/user/file.json`.
|
|
78
|
+
*/
|
|
79
|
+
function rejectPathTraversal(rawPath, fieldName) {
|
|
80
|
+
// Normalize to forward slashes for cross-platform check
|
|
81
|
+
const normalized = rawPath.replace(/\\/g, '/');
|
|
82
|
+
if (normalized.includes('../') || normalized === '..' || normalized.endsWith('/..')) {
|
|
83
|
+
const err = new errors_1.CliError(`${fieldName} contains a path traversal sequence ('../'). ` +
|
|
84
|
+
'Use a direct path instead.', errors_1.ExitCodes.INVALID_INPUT);
|
|
85
|
+
err.code = errors_1.ErrorCodes.INVALID_INPUT;
|
|
86
|
+
throw err;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Sanitize a secret value: strip dangerous control chars but keep \n, \r, \t
|
|
91
|
+
* (which appear in PEM keys, multi-line tokens, etc.).
|
|
92
|
+
*
|
|
93
|
+
* Returns the cleaned value. Does NOT throw — secrets may legitimately contain
|
|
94
|
+
* special characters, so we strip silently per the DX-29 spec.
|
|
95
|
+
*/
|
|
96
|
+
function sanitizeSecretValue(value) {
|
|
97
|
+
// eslint-disable-next-line no-control-regex
|
|
98
|
+
return value.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
|
|
99
|
+
}
|
package/dist/lib/validate.js
CHANGED
|
@@ -19,6 +19,8 @@ const path_1 = __importDefault(require("path"));
|
|
|
19
19
|
const yaml_1 = __importDefault(require("yaml"));
|
|
20
20
|
const publish_1 = require("../commands/publish");
|
|
21
21
|
const bundle_1 = require("./bundle");
|
|
22
|
+
const llm_1 = require("./llm");
|
|
23
|
+
const sanitize_1 = require("./sanitize");
|
|
22
24
|
// ── Helpers ────────────────────────────────────────────────────────────
|
|
23
25
|
function canonicalizeManifestType(typeValue) {
|
|
24
26
|
const rawType = (typeValue || 'agent').trim().toLowerCase();
|
|
@@ -54,6 +56,14 @@ function inferExecutionEngine(manifest, rawType) {
|
|
|
54
56
|
return { engine: 'direct_llm', conflict: false };
|
|
55
57
|
}
|
|
56
58
|
function validateNameFormat(name, issues, file) {
|
|
59
|
+
// DX-29: reject control chars in agent names
|
|
60
|
+
try {
|
|
61
|
+
(0, sanitize_1.rejectControlChars)(name, 'agent name');
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
issues.push({ level: 'error', message: err.message, file });
|
|
65
|
+
return; // no point checking format if control chars present
|
|
66
|
+
}
|
|
57
67
|
const nameRegex = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
58
68
|
if (name.length < 2 || name.length > 50) {
|
|
59
69
|
issues.push({ level: 'error', message: 'Agent name must be 2-50 characters', file });
|
|
@@ -175,6 +185,13 @@ async function validateManifest(projectDir, manifest, issues, metadata, options)
|
|
|
175
185
|
file: 'orchagent.json',
|
|
176
186
|
});
|
|
177
187
|
}
|
|
188
|
+
// ── Model ID validation (DX-17) ──
|
|
189
|
+
if (manifest.default_models && typeof manifest.default_models === 'object') {
|
|
190
|
+
const modelWarnings = (0, llm_1.validateModelIds)(manifest.default_models);
|
|
191
|
+
for (const w of modelWarnings) {
|
|
192
|
+
issues.push({ level: 'warning', message: w.message, file: 'orchagent.json' });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
178
195
|
// ── Misplaced manifest fields ──
|
|
179
196
|
const manifestFields = ['manifest_version', 'dependencies', 'max_hops', 'timeout_ms', 'per_call_downstream_cap'];
|
|
180
197
|
const misplacedFields = manifestFields.filter(f => f in manifest && !manifest.manifest);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orchagent/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.120",
|
|
4
4
|
"description": "Command-line interface for orchagent — deploy and run AI agents for your team",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "orchagent <hello@orchagent.io>",
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
},
|
|
31
31
|
"files": [
|
|
32
32
|
"dist",
|
|
33
|
-
"src/resources"
|
|
33
|
+
"src/resources",
|
|
34
|
+
"AGENT_GUIDE.md"
|
|
34
35
|
],
|
|
35
36
|
"scripts": {
|
|
36
37
|
"build": "tsc -p tsconfig.json",
|