agent-first-data 0.14.2 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -21
- package/assets/protocol-v1.schema.json +84 -0
- package/assets/registry.json +260 -0
- package/assets/skills/agent-first-data/SKILL.md +64 -0
- package/assets/skills/agent-first-data/references/protocol-v1.schema.json +66 -0
- package/assets/skills/agent-first-data/references/registry.json +260 -0
- package/assets/skills/agent-first-data/references/rules.md +347 -0
- package/dist/cli.d.ts +48 -14
- package/dist/cli.js +134 -36
- package/dist/format.d.ts +152 -55
- package/dist/format.js +652 -144
- package/dist/index.d.ts +2 -4
- package/dist/index.js +2 -4
- package/dist/stream_redirect.d.ts +21 -0
- package/dist/stream_redirect.js +128 -0
- package/package.json +12 -7
- package/dist/afdata_logging.d.ts +0 -44
- package/dist/afdata_logging.js +0 -90
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agent-First Data (AFDATA) — suffix-driven output formatting and protocol templates.
|
|
3
3
|
*/
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export { type OutputFormat, cliParseOutput, cliParseLogFilters, cliOutput, cliOutputWithOptions, buildCliError, buildCliVersion, cliRenderVersion, cliHandleVersionOrContinue, } from "./cli.js";
|
|
7
|
-
export { type SkillSpec, type SkillAction, type SkillScope, type SkillAgentSelection, type SkillAgent, type SkillOptions, type SkillTargetStatus, type SkillUninstallStatus, type SkillReport, SkillError, runSkillAdmin, } from "./skill.js";
|
|
4
|
+
export { jsonResult, jsonError, jsonProgress, jsonLog, Event, EventBuildError, JsonResultBuilder, JsonErrorBuilder, JsonProgressBuilder, JsonLogBuilder, type LogLevel, validateProtocolEvent, validateProtocolStream, decodeProtocolEvent, type DecodedEvent, type DecodedResult, type DecodedError, type DecodedProgress, type DecodedLog, EventDecodeError, type JsonValue, RedactionPolicy, OutputStyle, type OutputOptions, outputOptionsForPolicy, outputJson, outputYaml, outputPlain, redactedValue, redactUrlSecrets, parseSize, normalizeUtcOffset, isValidRfc3339Date, isValidRfc3339Time, } from "./format.js";
|
|
5
|
+
export { type OutputFormat, type CliEventWriter, CliEmitter, LogFilters, cliParseOutput, cliParseLogFilters, cliOutput, buildCliError, buildCliVersion, cliRenderVersion, cliHandleVersionOrContinue, } from "./cli.js";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agent-First Data (AFDATA) — suffix-driven output formatting and protocol templates.
|
|
3
3
|
*/
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export { cliParseOutput, cliParseLogFilters, cliOutput, cliOutputWithOptions, buildCliError, buildCliVersion, cliRenderVersion, cliHandleVersionOrContinue, } from "./cli.js";
|
|
7
|
-
export { SkillError, runSkillAdmin, } from "./skill.js";
|
|
4
|
+
export { jsonResult, jsonError, jsonProgress, jsonLog, Event, EventBuildError, JsonResultBuilder, JsonErrorBuilder, JsonProgressBuilder, JsonLogBuilder, validateProtocolEvent, validateProtocolStream, decodeProtocolEvent, EventDecodeError, RedactionPolicy, OutputStyle, outputOptionsForPolicy, outputJson, outputYaml, outputPlain, redactedValue, redactUrlSecrets, parseSize, normalizeUtcOffset, isValidRfc3339Date, isValidRfc3339Time, } from "./format.js";
|
|
5
|
+
export { CliEmitter, LogFilters, cliParseOutput, cliParseLogFilters, cliOutput, buildCliError, buildCliVersion, cliRenderVersion, cliHandleVersionOrContinue, } from "./cli.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional stdout/stderr file redirection for AFDATA CLIs.
|
|
3
|
+
*
|
|
4
|
+
* This redirects Node's process stdout/stderr file descriptors to append-only
|
|
5
|
+
* files. It is not an AFDATA formatter and does not convert stderr diagnostics
|
|
6
|
+
* into JSON.
|
|
7
|
+
*/
|
|
8
|
+
export declare const STDOUT_FILE_ARG = "--stdout-file";
|
|
9
|
+
export declare const STDERR_FILE_ARG = "--stderr-file";
|
|
10
|
+
export interface StreamRedirectConfig {
|
|
11
|
+
stdoutFile?: string;
|
|
12
|
+
stderrFile?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface InstalledStreamRedirect {
|
|
15
|
+
stdoutFile?: string;
|
|
16
|
+
stderrFile?: string;
|
|
17
|
+
processLifetime: true;
|
|
18
|
+
}
|
|
19
|
+
export declare function configFromRawArgs(args: readonly string[]): StreamRedirectConfig | undefined;
|
|
20
|
+
export declare function installStreamRedirectFromRawArgs(args?: string[]): InstalledStreamRedirect | undefined;
|
|
21
|
+
export declare function installStreamRedirect(config: StreamRedirectConfig): InstalledStreamRedirect | undefined;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional stdout/stderr file redirection for AFDATA CLIs.
|
|
3
|
+
*
|
|
4
|
+
* This redirects Node's process stdout/stderr file descriptors to append-only
|
|
5
|
+
* files. It is not an AFDATA formatter and does not convert stderr diagnostics
|
|
6
|
+
* into JSON.
|
|
7
|
+
*/
|
|
8
|
+
import { closeSync, constants, existsSync, lstatSync, openSync } from "node:fs";
|
|
9
|
+
export const STDOUT_FILE_ARG = "--stdout-file";
|
|
10
|
+
export const STDERR_FILE_ARG = "--stderr-file";
|
|
11
|
+
let installed = false;
|
|
12
|
+
const fillerFds = [];
|
|
13
|
+
export function configFromRawArgs(args) {
|
|
14
|
+
const config = {};
|
|
15
|
+
for (let i = 0; i < args.length; i++) {
|
|
16
|
+
const arg = args[i];
|
|
17
|
+
if (arg === "--")
|
|
18
|
+
break;
|
|
19
|
+
if (arg === STDOUT_FILE_ARG) {
|
|
20
|
+
const [value, next] = takeValue(args, i, STDOUT_FILE_ARG);
|
|
21
|
+
config.stdoutFile = value;
|
|
22
|
+
i = next;
|
|
23
|
+
}
|
|
24
|
+
else if (arg.startsWith(`${STDOUT_FILE_ARG}=`)) {
|
|
25
|
+
config.stdoutFile = arg.slice(STDOUT_FILE_ARG.length + 1);
|
|
26
|
+
}
|
|
27
|
+
else if (arg === STDERR_FILE_ARG) {
|
|
28
|
+
const [value, next] = takeValue(args, i, STDERR_FILE_ARG);
|
|
29
|
+
config.stderrFile = value;
|
|
30
|
+
i = next;
|
|
31
|
+
}
|
|
32
|
+
else if (arg.startsWith(`${STDERR_FILE_ARG}=`)) {
|
|
33
|
+
config.stderrFile = arg.slice(STDERR_FILE_ARG.length + 1);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
validateConfig(config);
|
|
37
|
+
return config.stdoutFile === undefined && config.stderrFile === undefined ? undefined : config;
|
|
38
|
+
}
|
|
39
|
+
export function installStreamRedirectFromRawArgs(args = process.argv.slice(2)) {
|
|
40
|
+
const config = configFromRawArgs(args);
|
|
41
|
+
return config === undefined ? undefined : installStreamRedirect(config);
|
|
42
|
+
}
|
|
43
|
+
export function installStreamRedirect(config) {
|
|
44
|
+
validateConfig(config);
|
|
45
|
+
if (config.stdoutFile === undefined && config.stderrFile === undefined) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
if (installed) {
|
|
49
|
+
throw new Error("stream redirection already installed");
|
|
50
|
+
}
|
|
51
|
+
installed = true;
|
|
52
|
+
// Validate both files before changing fd 1/2. Node does not expose dup2, so
|
|
53
|
+
// this helper is intended for process-lifetime CLI setup rather than scoped
|
|
54
|
+
// redirection that can be restored later.
|
|
55
|
+
validateWritable(config.stdoutFile);
|
|
56
|
+
validateWritable(config.stderrFile);
|
|
57
|
+
redirectFd(1, config.stdoutFile);
|
|
58
|
+
redirectFd(2, config.stderrFile);
|
|
59
|
+
return {
|
|
60
|
+
stdoutFile: config.stdoutFile,
|
|
61
|
+
stderrFile: config.stderrFile,
|
|
62
|
+
processLifetime: true,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function validateConfig(config) {
|
|
66
|
+
if (config.stdoutFile === "") {
|
|
67
|
+
throw new Error("--stdout-file must not be empty");
|
|
68
|
+
}
|
|
69
|
+
if (config.stderrFile === "") {
|
|
70
|
+
throw new Error("--stderr-file must not be empty");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function takeValue(args, idx, flag) {
|
|
74
|
+
const next = idx + 1;
|
|
75
|
+
const value = args[next];
|
|
76
|
+
if (value === undefined || value.startsWith("--")) {
|
|
77
|
+
throw new Error(`${flag} requires a value`);
|
|
78
|
+
}
|
|
79
|
+
return [value, next];
|
|
80
|
+
}
|
|
81
|
+
function validateWritable(path) {
|
|
82
|
+
if (path === undefined)
|
|
83
|
+
return;
|
|
84
|
+
const fd = openAppendSecure(path);
|
|
85
|
+
closeSync(fd);
|
|
86
|
+
}
|
|
87
|
+
function redirectFd(targetFd, path) {
|
|
88
|
+
if (path === undefined)
|
|
89
|
+
return;
|
|
90
|
+
fillLowerFds(targetFd);
|
|
91
|
+
try {
|
|
92
|
+
closeSync(targetFd);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// The target fd may already be closed in unusual embedding environments.
|
|
96
|
+
}
|
|
97
|
+
const fd = openAppendSecure(path);
|
|
98
|
+
if (fd !== targetFd) {
|
|
99
|
+
closeSync(fd);
|
|
100
|
+
throw new Error(`failed to redirect fd ${targetFd} to ${path}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function openAppendSecure(path) {
|
|
104
|
+
const nofollow = constants.O_NOFOLLOW ?? 0;
|
|
105
|
+
if (existsSync(path)) {
|
|
106
|
+
const stat = lstatSync(path);
|
|
107
|
+
if (stat.isSymbolicLink()) {
|
|
108
|
+
throw new Error("stream redirection target must not be a symbolic link");
|
|
109
|
+
}
|
|
110
|
+
if (!stat.isFile()) {
|
|
111
|
+
throw new Error("stream redirection target must be a regular file");
|
|
112
|
+
}
|
|
113
|
+
return openSync(path, constants.O_WRONLY | constants.O_APPEND | nofollow);
|
|
114
|
+
}
|
|
115
|
+
return openSync(path, constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT | constants.O_EXCL | nofollow, 0o600);
|
|
116
|
+
}
|
|
117
|
+
function fillLowerFds(targetFd) {
|
|
118
|
+
const nullDevice = process.platform === "win32" ? "NUL" : "/dev/null";
|
|
119
|
+
while (true) {
|
|
120
|
+
const fd = openSync(nullDevice, "r");
|
|
121
|
+
if (fd < targetFd) {
|
|
122
|
+
fillerFds.push(fd);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
closeSync(fd);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-first-data",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "A naming convention that lets AI agents understand your data without being told what it means.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -15,13 +15,18 @@
|
|
|
15
15
|
"import": "./dist/index.js",
|
|
16
16
|
"types": "./dist/index.d.ts"
|
|
17
17
|
},
|
|
18
|
-
"./
|
|
19
|
-
"import": "./dist/
|
|
20
|
-
"types": "./dist/
|
|
18
|
+
"./skill": {
|
|
19
|
+
"import": "./dist/skill.js",
|
|
20
|
+
"types": "./dist/skill.d.ts"
|
|
21
|
+
},
|
|
22
|
+
"./stream-redirect": {
|
|
23
|
+
"import": "./dist/stream_redirect.js",
|
|
24
|
+
"types": "./dist/stream_redirect.d.ts"
|
|
21
25
|
}
|
|
22
26
|
},
|
|
23
27
|
"files": [
|
|
24
28
|
"dist",
|
|
29
|
+
"assets",
|
|
25
30
|
"README.md"
|
|
26
31
|
],
|
|
27
32
|
"scripts": {
|
|
@@ -30,8 +35,8 @@
|
|
|
30
35
|
"test": "tsx --test src/*.test.ts examples/agent_cli.ts"
|
|
31
36
|
},
|
|
32
37
|
"devDependencies": {
|
|
33
|
-
"@types/node": "^
|
|
34
|
-
"tsx": "^4.
|
|
35
|
-
"typescript": "^
|
|
38
|
+
"@types/node": "^26.1.1",
|
|
39
|
+
"tsx": "^4.23.0",
|
|
40
|
+
"typescript": "^7.0.2"
|
|
36
41
|
}
|
|
37
42
|
}
|
package/dist/afdata_logging.d.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AFDATA-compliant structured logging.
|
|
3
|
-
*
|
|
4
|
-
* Outputs log events using agent-first-data formatting functions:
|
|
5
|
-
* - JSON: single-line JSONL via outputJson (secrets redacted, original keys)
|
|
6
|
-
* - Plain: single-line logfmt via outputPlain (keys stripped, values formatted)
|
|
7
|
-
* - YAML: multi-line via outputYaml (keys stripped, values formatted)
|
|
8
|
-
*
|
|
9
|
-
* Span fields are carried via AsyncLocalStorage (async-safe).
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* import { log, span, initJson, initPlain, initYaml } from "agent-first-data/afdata_logging";
|
|
13
|
-
* initJson(); // or initPlain() or initYaml()
|
|
14
|
-
* log.info("Server started");
|
|
15
|
-
* await span({ request_id: "abc" }, async () => {
|
|
16
|
-
* log.info("Processing", { domain: "example.com" });
|
|
17
|
-
* });
|
|
18
|
-
*/
|
|
19
|
-
type LoggingRedactionOptions = {
|
|
20
|
-
secretNames?: readonly string[];
|
|
21
|
-
};
|
|
22
|
-
/** Set log output to single-line JSONL (secrets redacted, original keys). */
|
|
23
|
-
export declare function initJson(options?: LoggingRedactionOptions): void;
|
|
24
|
-
/** Set log output to single-line logfmt (keys stripped, values formatted). */
|
|
25
|
-
export declare function initPlain(options?: LoggingRedactionOptions): void;
|
|
26
|
-
/** Set log output to multi-line YAML (keys stripped, values formatted). */
|
|
27
|
-
export declare function initYaml(options?: LoggingRedactionOptions): void;
|
|
28
|
-
/**
|
|
29
|
-
* AFDATA logger. Each method outputs a single log line to stdout.
|
|
30
|
-
* Format is controlled by initJson/initPlain/initYaml (default: JSON).
|
|
31
|
-
*/
|
|
32
|
-
export declare const log: {
|
|
33
|
-
trace: (msg: string, fields?: Record<string, unknown>) => void;
|
|
34
|
-
debug: (msg: string, fields?: Record<string, unknown>) => void;
|
|
35
|
-
info: (msg: string, fields?: Record<string, unknown>) => void;
|
|
36
|
-
warn: (msg: string, fields?: Record<string, unknown>) => void;
|
|
37
|
-
error: (msg: string, fields?: Record<string, unknown>) => void;
|
|
38
|
-
};
|
|
39
|
-
/**
|
|
40
|
-
* Run fn within a span that adds fields to all log events.
|
|
41
|
-
* Spans nest: inner spans inherit and can override outer span fields.
|
|
42
|
-
*/
|
|
43
|
-
export declare function span<T>(fields: Record<string, unknown>, fn: () => T): T;
|
|
44
|
-
export {};
|
package/dist/afdata_logging.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AFDATA-compliant structured logging.
|
|
3
|
-
*
|
|
4
|
-
* Outputs log events using agent-first-data formatting functions:
|
|
5
|
-
* - JSON: single-line JSONL via outputJson (secrets redacted, original keys)
|
|
6
|
-
* - Plain: single-line logfmt via outputPlain (keys stripped, values formatted)
|
|
7
|
-
* - YAML: multi-line via outputYaml (keys stripped, values formatted)
|
|
8
|
-
*
|
|
9
|
-
* Span fields are carried via AsyncLocalStorage (async-safe).
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* import { log, span, initJson, initPlain, initYaml } from "agent-first-data/afdata_logging";
|
|
13
|
-
* initJson(); // or initPlain() or initYaml()
|
|
14
|
-
* log.info("Server started");
|
|
15
|
-
* await span({ request_id: "abc" }, async () => {
|
|
16
|
-
* log.info("Processing", { domain: "example.com" });
|
|
17
|
-
* });
|
|
18
|
-
*/
|
|
19
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
20
|
-
import { outputJsonWithOptions, outputYamlWithOptions, outputPlainWithOptions, } from "./format.js";
|
|
21
|
-
const spanStore = new AsyncLocalStorage();
|
|
22
|
-
let currentFormat = "json";
|
|
23
|
-
let currentRedaction = {};
|
|
24
|
-
function setFormat(format, options) {
|
|
25
|
-
currentFormat = format;
|
|
26
|
-
currentRedaction = options?.secretNames ? { secretNames: options.secretNames } : {};
|
|
27
|
-
}
|
|
28
|
-
/** Set log output to single-line JSONL (secrets redacted, original keys). */
|
|
29
|
-
export function initJson(options) { setFormat("json", options); }
|
|
30
|
-
/** Set log output to single-line logfmt (keys stripped, values formatted). */
|
|
31
|
-
export function initPlain(options) { setFormat("plain", options); }
|
|
32
|
-
/** Set log output to multi-line YAML (keys stripped, values formatted). */
|
|
33
|
-
export function initYaml(options) { setFormat("yaml", options); }
|
|
34
|
-
function emit(level, message, fields) {
|
|
35
|
-
const entry = {
|
|
36
|
-
timestamp_epoch_ms: Date.now(),
|
|
37
|
-
message,
|
|
38
|
-
code: "log",
|
|
39
|
-
level,
|
|
40
|
-
};
|
|
41
|
-
// Span fields (from AsyncLocalStorage)
|
|
42
|
-
const spanFields = spanStore.getStore();
|
|
43
|
-
if (spanFields) {
|
|
44
|
-
for (const [k, v] of Object.entries(spanFields)) {
|
|
45
|
-
entry[k] = v;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
// Event fields override span fields, except protocol code is always "log".
|
|
49
|
-
if (fields) {
|
|
50
|
-
for (const [k, v] of Object.entries(fields)) {
|
|
51
|
-
if (k === "code")
|
|
52
|
-
continue;
|
|
53
|
-
entry[k] = v;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
// Format using the library's own output functions
|
|
57
|
-
const value = entry;
|
|
58
|
-
let line;
|
|
59
|
-
switch (currentFormat) {
|
|
60
|
-
case "plain":
|
|
61
|
-
line = outputPlainWithOptions(value, { redaction: currentRedaction });
|
|
62
|
-
break;
|
|
63
|
-
case "yaml":
|
|
64
|
-
line = outputYamlWithOptions(value, { redaction: currentRedaction });
|
|
65
|
-
break;
|
|
66
|
-
default:
|
|
67
|
-
line = outputJsonWithOptions(value, { redaction: currentRedaction });
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
process.stdout.write(line + "\n");
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* AFDATA logger. Each method outputs a single log line to stdout.
|
|
74
|
-
* Format is controlled by initJson/initPlain/initYaml (default: JSON).
|
|
75
|
-
*/
|
|
76
|
-
export const log = {
|
|
77
|
-
trace: (msg, fields) => emit("trace", msg, fields),
|
|
78
|
-
debug: (msg, fields) => emit("debug", msg, fields),
|
|
79
|
-
info: (msg, fields) => emit("info", msg, fields),
|
|
80
|
-
warn: (msg, fields) => emit("warn", msg, fields),
|
|
81
|
-
error: (msg, fields) => emit("error", msg, fields),
|
|
82
|
-
};
|
|
83
|
-
/**
|
|
84
|
-
* Run fn within a span that adds fields to all log events.
|
|
85
|
-
* Spans nest: inner spans inherit and can override outer span fields.
|
|
86
|
-
*/
|
|
87
|
-
export function span(fields, fn) {
|
|
88
|
-
const parent = spanStore.getStore() ?? {};
|
|
89
|
-
return spanStore.run({ ...parent, ...fields }, fn);
|
|
90
|
-
}
|