@kruntime/komputer 0.1.1 → 0.1.3
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/agent-session.js +192 -7
- package/dist/agent-session.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/komputer.js +29 -2
- package/dist/komputer.js.map +1 -1
- package/dist/shell/builtins/commands/filesystem.js +22 -20
- package/dist/shell/builtins/commands/filesystem.js.map +1 -1
- package/dist/shell/builtins/commands/network.js +3 -2
- package/dist/shell/builtins/commands/network.js.map +1 -1
- package/dist/shell/builtins/commands/process.js +9 -7
- package/dist/shell/builtins/commands/process.js.map +1 -1
- package/dist/shell/builtins/commands/system.js +27 -24
- package/dist/shell/builtins/commands/system.js.map +1 -1
- package/dist/shell/builtins/commands/text.js +20 -18
- package/dist/shell/builtins/commands/text.js.map +1 -1
- package/dist/shell/builtins/run.d.ts +9 -1
- package/dist/shell/builtins/run.js +12 -0
- package/dist/shell/builtins/run.js.map +1 -1
- package/dist/shell/builtins/runtime.d.ts +6 -2
- package/dist/shell/builtins/runtime.js +10 -3
- package/dist/shell/builtins/runtime.js.map +1 -1
- package/dist/types.d.ts +33 -0
- package/package.json +1 -1
package/dist/agent-session.js
CHANGED
|
@@ -2646,6 +2646,9 @@ export class AgentSession {
|
|
|
2646
2646
|
throw new Error(`command not found: ${name}`);
|
|
2647
2647
|
if (command.agent && command.agent !== this.agentName)
|
|
2648
2648
|
throw new Error(`command ${name} belongs to agent ${command.agent}`);
|
|
2649
|
+
if (isCommandHelpInput(input)) {
|
|
2650
|
+
return { type: 'stdout', text: `${commandHelpText(name, command)}\n`, code: 0 };
|
|
2651
|
+
}
|
|
2649
2652
|
const by = normalizeBy(options.by || (typeof input.by === 'string' ? input.by : undefined));
|
|
2650
2653
|
const toolName = `command:${name}`;
|
|
2651
2654
|
const before = await this.runHook('tool.before', { tool: toolName, command: name, input, by });
|
|
@@ -2662,7 +2665,16 @@ export class AgentSession {
|
|
|
2662
2665
|
throw new Error(authority.reason);
|
|
2663
2666
|
}
|
|
2664
2667
|
const toolUseId = id('tool');
|
|
2665
|
-
const
|
|
2668
|
+
const parsed = parseCommandInput(name, command, currentInput);
|
|
2669
|
+
const ctx = this.runtime.runtimeContext(this, {
|
|
2670
|
+
kind: 'command',
|
|
2671
|
+
name,
|
|
2672
|
+
input: currentInput,
|
|
2673
|
+
args: parsed.args,
|
|
2674
|
+
argv: parsed.argv,
|
|
2675
|
+
stdin: parsed.stdin,
|
|
2676
|
+
env: parsed.env,
|
|
2677
|
+
});
|
|
2666
2678
|
await this.emit({ type: 'tool', status: 'requested', tool: toolName, input: currentInput, opId: toolUseId });
|
|
2667
2679
|
await this.trace('tool.started', { name, input: currentInput, by, toolUseId });
|
|
2668
2680
|
let result;
|
|
@@ -2677,20 +2689,30 @@ export class AgentSession {
|
|
|
2677
2689
|
result = invocationMode ? await this.withTemporaryMode(invocationMode, runShell) : await runShell();
|
|
2678
2690
|
}
|
|
2679
2691
|
else if (command.kind === 'markdown') {
|
|
2680
|
-
const
|
|
2692
|
+
const prompt = String(command.prompt ?? '');
|
|
2693
|
+
const rendered = renderMarkdownCommandPrompt(prompt, parsed.args, parsed.stdin);
|
|
2694
|
+
const args = hasCommandTemplate(prompt) || Object.keys(currentInput).length === 0
|
|
2695
|
+
? ''
|
|
2696
|
+
: `\n\nInput:\n${JSON.stringify(currentInput, null, 2)}`;
|
|
2681
2697
|
const invocationMode = command.readonly === true
|
|
2682
2698
|
? 'readonly'
|
|
2683
2699
|
: typeof command.mode === 'string'
|
|
2684
2700
|
? command.mode
|
|
2685
2701
|
: undefined;
|
|
2686
|
-
const runMarkdown = () => this.run(`${
|
|
2702
|
+
const runMarkdown = () => this.run(`${rendered}${args}`, { by: options.by || `command:${name}`, model: typeof command.model === 'string' ? command.model : undefined });
|
|
2687
2703
|
result = invocationMode ? await this.withTemporaryMode(invocationMode, runMarkdown) : await runMarkdown();
|
|
2688
2704
|
}
|
|
2689
2705
|
else {
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2706
|
+
const main = command.main ?? command.handler;
|
|
2707
|
+
if (!main)
|
|
2708
|
+
throw new Error(`command has no main: ${name}`);
|
|
2709
|
+
const runModule = () => this.withFsActor(options.by ? by : `command:${name}`, () => Promise.resolve(main(ctx, parsed.args)));
|
|
2710
|
+
const invocationMode = command.readonly === true
|
|
2711
|
+
? 'readonly'
|
|
2712
|
+
: typeof command.mode === 'string'
|
|
2713
|
+
? command.mode
|
|
2714
|
+
: undefined;
|
|
2715
|
+
result = invocationMode ? await this.withTemporaryMode(invocationMode, runModule) : await runModule();
|
|
2694
2716
|
}
|
|
2695
2717
|
const after = await this.runHook('tool.after', { tool: toolName, command: name, input: currentInput, result, by });
|
|
2696
2718
|
if (after.rejected) {
|
|
@@ -2992,6 +3014,169 @@ function commandErrorText(command, error) {
|
|
|
2992
3014
|
function isHelpArg(value) {
|
|
2993
3015
|
return value === '--help' || value === '-h';
|
|
2994
3016
|
}
|
|
3017
|
+
function isCommandHelpInput(input) {
|
|
3018
|
+
const argv = stringArrayField(input, 'argv') || [];
|
|
3019
|
+
return argv.length === 1 && isHelpArg(argv[0]);
|
|
3020
|
+
}
|
|
3021
|
+
function parseCommandInput(name, command, input) {
|
|
3022
|
+
const argv = stringArrayField(input, 'argv') || [];
|
|
3023
|
+
const stdin = typeof input.stdin === 'string' ? input.stdin : '';
|
|
3024
|
+
const envRecord = recordField(input, 'env');
|
|
3025
|
+
const env = envRecord ? stringRecord(envRecord) : undefined;
|
|
3026
|
+
if (command.args === 'none') {
|
|
3027
|
+
const extra = argv.filter((arg) => arg !== '--');
|
|
3028
|
+
if (extra.length)
|
|
3029
|
+
throw new Error(`${name}: unexpected argument: ${extra[0]}`);
|
|
3030
|
+
return { argv, stdin, env, args: {} };
|
|
3031
|
+
}
|
|
3032
|
+
if (!command.args || command.args === 'argv')
|
|
3033
|
+
return { argv, stdin, env, args: input };
|
|
3034
|
+
return { argv, stdin, env, args: parseStructuredCommandArgs(name, command.args, input, argv) };
|
|
3035
|
+
}
|
|
3036
|
+
function parseStructuredCommandArgs(name, specs, input, argv) {
|
|
3037
|
+
const parsed = {};
|
|
3038
|
+
const options = commandOptionSpecs(specs);
|
|
3039
|
+
for (const [key, spec] of Object.entries(specs)) {
|
|
3040
|
+
if ('defaultValue' in spec)
|
|
3041
|
+
parsed[key] = spec.defaultValue;
|
|
3042
|
+
else if ('default' in spec && typeof spec.default !== 'function')
|
|
3043
|
+
parsed[key] = spec.default;
|
|
3044
|
+
}
|
|
3045
|
+
for (const [key, value] of Object.entries(input)) {
|
|
3046
|
+
if (key !== 'argv' && key !== 'stdin' && key !== 'env' && specs[key])
|
|
3047
|
+
parsed[key] = value;
|
|
3048
|
+
}
|
|
3049
|
+
const positional = Object.entries(specs)
|
|
3050
|
+
.filter(([, spec]) => typeof spec.position === 'number')
|
|
3051
|
+
.sort((a, b) => Number(a[1].position) - Number(b[1].position));
|
|
3052
|
+
let positionalIndex = 0;
|
|
3053
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
3054
|
+
const token = argv[index];
|
|
3055
|
+
if (token === '--') {
|
|
3056
|
+
for (const rest of argv.slice(index + 1))
|
|
3057
|
+
assignPositional(name, parsed, positional, positionalIndex++, rest);
|
|
3058
|
+
break;
|
|
3059
|
+
}
|
|
3060
|
+
if (token.startsWith('--')) {
|
|
3061
|
+
const body = token.slice(2);
|
|
3062
|
+
const eq = body.indexOf('=');
|
|
3063
|
+
const option = eq >= 0 ? body.slice(0, eq) : body;
|
|
3064
|
+
const resolved = options.get(option);
|
|
3065
|
+
if (!resolved)
|
|
3066
|
+
throw new Error(`${name}: unsupported option: --${option}`);
|
|
3067
|
+
const [key, spec] = resolved;
|
|
3068
|
+
if (spec.type === 'boolean' && eq < 0) {
|
|
3069
|
+
parsed[key] = true;
|
|
3070
|
+
continue;
|
|
3071
|
+
}
|
|
3072
|
+
const value = eq >= 0 ? body.slice(eq + 1) : argv[++index];
|
|
3073
|
+
if (value === undefined)
|
|
3074
|
+
throw new Error(`${name}: option requires an argument: --${key}`);
|
|
3075
|
+
assignCommandArg(name, parsed, key, spec, value);
|
|
3076
|
+
continue;
|
|
3077
|
+
}
|
|
3078
|
+
const eq = token.indexOf('=');
|
|
3079
|
+
if (eq > 0 && specs[token.slice(0, eq)]) {
|
|
3080
|
+
const key = token.slice(0, eq);
|
|
3081
|
+
assignCommandArg(name, parsed, key, specs[key], token.slice(eq + 1));
|
|
3082
|
+
continue;
|
|
3083
|
+
}
|
|
3084
|
+
assignPositional(name, parsed, positional, positionalIndex++, token);
|
|
3085
|
+
}
|
|
3086
|
+
for (const [key, spec] of Object.entries(specs)) {
|
|
3087
|
+
if (spec.required === true && (parsed[key] === undefined || parsed[key] === '')) {
|
|
3088
|
+
throw new Error(`${name}: missing required argument: --${key}`);
|
|
3089
|
+
}
|
|
3090
|
+
if (parsed[key] !== undefined)
|
|
3091
|
+
parsed[key] = coerceCommandArg(name, key, spec, parsed[key]);
|
|
3092
|
+
}
|
|
3093
|
+
return parsed;
|
|
3094
|
+
}
|
|
3095
|
+
function commandOptionSpecs(specs) {
|
|
3096
|
+
const options = new Map();
|
|
3097
|
+
for (const [key, spec] of Object.entries(specs)) {
|
|
3098
|
+
options.set(key, [key, spec]);
|
|
3099
|
+
const flag = typeof spec.flag === 'string' ? normalizeCommandFlag(spec.flag) : undefined;
|
|
3100
|
+
if (flag)
|
|
3101
|
+
options.set(flag, [key, spec]);
|
|
3102
|
+
}
|
|
3103
|
+
return options;
|
|
3104
|
+
}
|
|
3105
|
+
function normalizeCommandFlag(flag) {
|
|
3106
|
+
return flag.trim().replace(/^-+/, '');
|
|
3107
|
+
}
|
|
3108
|
+
function assignPositional(name, parsed, positional, index, value) {
|
|
3109
|
+
const entry = positional[index];
|
|
3110
|
+
if (!entry)
|
|
3111
|
+
throw new Error(`${name}: unexpected argument: ${String(value)}`);
|
|
3112
|
+
assignCommandArg(name, parsed, entry[0], entry[1], value);
|
|
3113
|
+
}
|
|
3114
|
+
function assignCommandArg(name, parsed, key, spec, value) {
|
|
3115
|
+
const coerced = coerceCommandArg(name, key, spec, value);
|
|
3116
|
+
if (spec.repeat) {
|
|
3117
|
+
const current = Array.isArray(parsed[key]) ? parsed[key] : parsed[key] === undefined ? [] : [parsed[key]];
|
|
3118
|
+
current.push(coerced);
|
|
3119
|
+
parsed[key] = current;
|
|
3120
|
+
return;
|
|
3121
|
+
}
|
|
3122
|
+
parsed[key] = coerced;
|
|
3123
|
+
}
|
|
3124
|
+
function coerceCommandArg(name, key, spec, value) {
|
|
3125
|
+
if (value === undefined)
|
|
3126
|
+
return value;
|
|
3127
|
+
if (spec.type === 'string' || spec.type === 'path')
|
|
3128
|
+
return String(value);
|
|
3129
|
+
if (spec.type === 'number') {
|
|
3130
|
+
const number = typeof value === 'number' ? value : Number(String(value));
|
|
3131
|
+
if (!Number.isFinite(number))
|
|
3132
|
+
throw new Error(`${name}: invalid value for --${key}: expected number`);
|
|
3133
|
+
return number;
|
|
3134
|
+
}
|
|
3135
|
+
if (spec.type === 'boolean') {
|
|
3136
|
+
if (typeof value === 'boolean')
|
|
3137
|
+
return value;
|
|
3138
|
+
const text = String(value).toLowerCase();
|
|
3139
|
+
if (text === 'true' || text === '1' || text === 'yes')
|
|
3140
|
+
return true;
|
|
3141
|
+
if (text === 'false' || text === '0' || text === 'no')
|
|
3142
|
+
return false;
|
|
3143
|
+
throw new Error(`${name}: invalid value for --${key}: expected boolean`);
|
|
3144
|
+
}
|
|
3145
|
+
if (spec.type === 'json') {
|
|
3146
|
+
if (typeof value !== 'string')
|
|
3147
|
+
return value;
|
|
3148
|
+
try {
|
|
3149
|
+
return JSON.parse(value);
|
|
3150
|
+
}
|
|
3151
|
+
catch {
|
|
3152
|
+
throw new Error(`${name}: invalid value for --${key}: expected json`);
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
return value;
|
|
3156
|
+
}
|
|
3157
|
+
function stringRecord(record) {
|
|
3158
|
+
const output = {};
|
|
3159
|
+
for (const [key, value] of Object.entries(record)) {
|
|
3160
|
+
if (typeof value === 'string')
|
|
3161
|
+
output[key] = value;
|
|
3162
|
+
}
|
|
3163
|
+
return output;
|
|
3164
|
+
}
|
|
3165
|
+
function hasCommandTemplate(prompt) {
|
|
3166
|
+
return /\{\{\s*(?:arg|args)\.[A-Za-z0-9_.-]+\s*\}\}|\{\{\s*stdin\s*\}\}/.test(prompt);
|
|
3167
|
+
}
|
|
3168
|
+
function renderMarkdownCommandPrompt(prompt, args, stdin) {
|
|
3169
|
+
return prompt.replace(/\{\{\s*((?:arg|args)\.([A-Za-z0-9_.-]+)|stdin)\s*\}\}/g, (_match, token, argName) => {
|
|
3170
|
+
if (token === 'stdin')
|
|
3171
|
+
return stdin;
|
|
3172
|
+
const value = argName ? args[argName] : undefined;
|
|
3173
|
+
if (value === undefined || value === null)
|
|
3174
|
+
return '';
|
|
3175
|
+
if (typeof value === 'string')
|
|
3176
|
+
return value;
|
|
3177
|
+
return JSON.stringify(value);
|
|
3178
|
+
});
|
|
3179
|
+
}
|
|
2995
3180
|
function isExpiredWorker(worker) {
|
|
2996
3181
|
const expiresAt = typeof worker.expiresAt === 'string' ? Date.parse(worker.expiresAt) : Number.NaN;
|
|
2997
3182
|
return Number.isFinite(expiresAt) && expiresAt <= Date.now();
|