@howlil/ez-agents 2.0.0 → 3.0.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/LICENSE +21 -21
- package/README.md +157 -110
- package/README.zh-CN.md +84 -84
- package/agents/ez-plan-checker.md +2 -2
- package/agents/ez-research-synthesizer.md +1 -1
- package/agents/ez-ui-auditor.md +0 -2
- package/agents/ez-ui-checker.md +2 -4
- package/agents/ez-ui-researcher.md +0 -2
- package/agents/ez-verifier.md +1 -1
- package/bin/install.js +211 -211
- package/commands/ez/debug.md +1 -1
- package/commands/ez/map-codebase.md +1 -1
- package/commands/ez/reapply-patches.md +3 -3
- package/commands/ez/research-phase.md +1 -1
- package/{get-shit-done → ez-agents}/bin/ez-tools.cjs +1 -1
- package/{get-shit-done → ez-agents}/bin/lib/assistant-adapter.cjs +205 -205
- package/{get-shit-done → ez-agents}/bin/lib/audit-exec.cjs +150 -150
- package/{get-shit-done → ez-agents}/bin/lib/auth.cjs +175 -175
- package/{get-shit-done → ez-agents}/bin/lib/circuit-breaker.cjs +118 -118
- package/{get-shit-done → ez-agents}/bin/lib/commands.cjs +666 -666
- package/{get-shit-done → ez-agents}/bin/lib/config.cjs +183 -183
- package/{get-shit-done → ez-agents}/bin/lib/core.cjs +495 -495
- package/{get-shit-done → ez-agents}/bin/lib/file-lock.cjs +236 -236
- package/{get-shit-done → ez-agents}/bin/lib/frontmatter.cjs +299 -299
- package/{get-shit-done → ez-agents}/bin/lib/fs-utils.cjs +153 -153
- package/{get-shit-done → ez-agents}/bin/lib/git-utils.cjs +203 -203
- package/{get-shit-done → ez-agents}/bin/lib/health-check.cjs +163 -163
- package/{get-shit-done → ez-agents}/bin/lib/index.cjs +113 -113
- package/{get-shit-done → ez-agents}/bin/lib/init.cjs +710 -710
- package/{get-shit-done → ez-agents}/bin/lib/logger.cjs +117 -117
- package/{get-shit-done → ez-agents}/bin/lib/milestone.cjs +241 -241
- package/{get-shit-done → ez-agents}/bin/lib/model-provider.cjs +146 -146
- package/{get-shit-done → ez-agents}/bin/lib/phase.cjs +908 -908
- package/{get-shit-done → ez-agents}/bin/lib/retry.cjs +119 -119
- package/{get-shit-done → ez-agents}/bin/lib/roadmap.cjs +305 -305
- package/{get-shit-done → ez-agents}/bin/lib/safe-exec.cjs +128 -128
- package/{get-shit-done → ez-agents}/bin/lib/safe-path.cjs +130 -130
- package/{get-shit-done → ez-agents}/bin/lib/state.cjs +721 -721
- package/{get-shit-done → ez-agents}/bin/lib/temp-file.cjs +239 -239
- package/{get-shit-done → ez-agents}/bin/lib/template.cjs +222 -222
- package/{get-shit-done → ez-agents}/bin/lib/test-file-lock.cjs +112 -112
- package/{get-shit-done → ez-agents}/bin/lib/test-graceful.cjs +93 -93
- package/{get-shit-done → ez-agents}/bin/lib/test-logger.cjs +60 -60
- package/{get-shit-done → ez-agents}/bin/lib/test-safe-exec.cjs +38 -38
- package/{get-shit-done → ez-agents}/bin/lib/test-safe-path.cjs +33 -33
- package/{get-shit-done → ez-agents}/bin/lib/test-temp-file.cjs +125 -125
- package/{get-shit-done → ez-agents}/bin/lib/timeout-exec.cjs +62 -62
- package/{get-shit-done → ez-agents}/bin/lib/verify.cjs +820 -820
- package/{get-shit-done → ez-agents}/references/checkpoints.md +776 -776
- package/{get-shit-done → ez-agents}/references/questioning.md +162 -162
- package/{get-shit-done → ez-agents}/references/tdd.md +263 -263
- package/{get-shit-done → ez-agents}/templates/codebase/concerns.md +310 -310
- package/{get-shit-done → ez-agents}/templates/codebase/conventions.md +307 -307
- package/{get-shit-done → ez-agents}/templates/codebase/integrations.md +280 -280
- package/{get-shit-done → ez-agents}/templates/codebase/stack.md +186 -186
- package/{get-shit-done → ez-agents}/templates/codebase/testing.md +480 -480
- package/{get-shit-done → ez-agents}/templates/config.json +37 -37
- package/{get-shit-done → ez-agents}/templates/continue-here.md +78 -78
- package/{get-shit-done → ez-agents}/templates/milestone-archive.md +123 -123
- package/{get-shit-done → ez-agents}/templates/milestone.md +115 -115
- package/{get-shit-done → ez-agents}/templates/requirements.md +231 -231
- package/{get-shit-done → ez-agents}/templates/research-project/ARCHITECTURE.md +204 -204
- package/{get-shit-done → ez-agents}/templates/research-project/FEATURES.md +147 -147
- package/{get-shit-done → ez-agents}/templates/research-project/PITFALLS.md +200 -200
- package/{get-shit-done → ez-agents}/templates/research-project/STACK.md +120 -120
- package/{get-shit-done → ez-agents}/templates/research-project/SUMMARY.md +170 -170
- package/{get-shit-done → ez-agents}/templates/retrospective.md +54 -54
- package/{get-shit-done → ez-agents}/templates/roadmap.md +202 -202
- package/{get-shit-done → ez-agents}/templates/summary-minimal.md +41 -41
- package/{get-shit-done → ez-agents}/templates/summary-standard.md +48 -48
- package/{get-shit-done → ez-agents}/templates/summary.md +248 -248
- package/{get-shit-done → ez-agents}/templates/user-setup.md +311 -311
- package/{get-shit-done → ez-agents}/templates/verification-report.md +322 -322
- package/{get-shit-done → ez-agents}/workflows/add-phase.md +112 -112
- package/{get-shit-done → ez-agents}/workflows/add-tests.md +351 -351
- package/{get-shit-done → ez-agents}/workflows/add-todo.md +158 -158
- package/{get-shit-done → ez-agents}/workflows/audit-milestone.md +332 -332
- package/{get-shit-done → ez-agents}/workflows/autonomous.md +743 -743
- package/{get-shit-done → ez-agents}/workflows/check-todos.md +177 -177
- package/{get-shit-done → ez-agents}/workflows/cleanup.md +152 -152
- package/{get-shit-done → ez-agents}/workflows/complete-milestone.md +766 -766
- package/ez-agents/workflows/debug.md +0 -0
- package/{get-shit-done → ez-agents}/workflows/diagnose-issues.md +219 -219
- package/{get-shit-done → ez-agents}/workflows/discovery-phase.md +289 -289
- package/{get-shit-done → ez-agents}/workflows/discuss-phase.md +762 -762
- package/{get-shit-done → ez-agents}/workflows/execute-phase.md +468 -468
- package/{get-shit-done → ez-agents}/workflows/execute-plan.md +483 -483
- package/{get-shit-done → ez-agents}/workflows/health.md +159 -159
- package/{get-shit-done → ez-agents}/workflows/help.md +492 -492
- package/{get-shit-done → ez-agents}/workflows/insert-phase.md +130 -130
- package/{get-shit-done → ez-agents}/workflows/list-phase-assumptions.md +178 -178
- package/{get-shit-done → ez-agents}/workflows/map-codebase.md +316 -316
- package/{get-shit-done → ez-agents}/workflows/new-milestone.md +384 -384
- package/{get-shit-done → ez-agents}/workflows/new-project.md +1111 -1111
- package/{get-shit-done → ez-agents}/workflows/node-repair.md +92 -92
- package/{get-shit-done → ez-agents}/workflows/pause-work.md +122 -122
- package/{get-shit-done → ez-agents}/workflows/plan-milestone-gaps.md +274 -274
- package/{get-shit-done → ez-agents}/workflows/plan-phase.md +651 -651
- package/{get-shit-done → ez-agents}/workflows/progress.md +382 -382
- package/{get-shit-done → ez-agents}/workflows/quick.md +610 -610
- package/{get-shit-done → ez-agents}/workflows/remove-phase.md +155 -155
- package/{get-shit-done → ez-agents}/workflows/research-phase.md +74 -74
- package/{get-shit-done → ez-agents}/workflows/resume-project.md +307 -307
- package/{get-shit-done → ez-agents}/workflows/set-profile.md +81 -81
- package/{get-shit-done → ez-agents}/workflows/settings.md +242 -242
- package/{get-shit-done → ez-agents}/workflows/stats.md +57 -57
- package/{get-shit-done → ez-agents}/workflows/transition.md +544 -544
- package/{get-shit-done → ez-agents}/workflows/ui-phase.md +290 -290
- package/{get-shit-done → ez-agents}/workflows/ui-review.md +157 -157
- package/{get-shit-done → ez-agents}/workflows/update.md +320 -320
- package/{get-shit-done → ez-agents}/workflows/validate-phase.md +167 -167
- package/{get-shit-done → ez-agents}/workflows/verify-phase.md +243 -243
- package/{get-shit-done → ez-agents}/workflows/verify-work.md +5 -5
- package/hooks/dist/ez-check-update.js +81 -0
- package/hooks/dist/ez-context-monitor.js +141 -0
- package/hooks/dist/ez-statusline.js +115 -0
- package/package.json +13 -3
- package/scripts/build-hooks.js +43 -43
- package/scripts/run-tests.cjs +29 -29
- /package/{get-shit-done → ez-agents}/references/continuation-format.md +0 -0
- /package/{get-shit-done → ez-agents}/references/decimal-phase-calculation.md +0 -0
- /package/{get-shit-done → ez-agents}/references/git-integration.md +0 -0
- /package/{get-shit-done → ez-agents}/references/git-planning-commit.md +0 -0
- /package/{get-shit-done → ez-agents}/references/model-profile-resolution.md +0 -0
- /package/{get-shit-done → ez-agents}/references/model-profiles.md +0 -0
- /package/{get-shit-done → ez-agents}/references/phase-argument-parsing.md +0 -0
- /package/{get-shit-done → ez-agents}/references/planning-config.md +0 -0
- /package/{get-shit-done → ez-agents}/references/ui-brand.md +0 -0
- /package/{get-shit-done → ez-agents}/references/verification-patterns.md +0 -0
- /package/{get-shit-done → ez-agents}/templates/DEBUG.md +0 -0
- /package/{get-shit-done → ez-agents}/templates/UAT.md +0 -0
- /package/{get-shit-done → ez-agents}/templates/UI-SPEC.md +0 -0
- /package/{get-shit-done → ez-agents}/templates/VALIDATION.md +0 -0
- /package/{get-shit-done → ez-agents}/templates/codebase/architecture.md +0 -0
- /package/{get-shit-done → ez-agents}/templates/codebase/structure.md +0 -0
- /package/{get-shit-done → ez-agents}/templates/context.md +0 -0
- /package/{get-shit-done → ez-agents}/templates/copilot-instructions.md +0 -0
- /package/{get-shit-done → ez-agents}/templates/debug-subagent-prompt.md +0 -0
- /package/{get-shit-done → ez-agents}/templates/discovery.md +0 -0
- /package/{get-shit-done → ez-agents}/templates/phase-prompt.md +0 -0
- /package/{get-shit-done → ez-agents}/templates/planner-subagent-prompt.md +0 -0
- /package/{get-shit-done → ez-agents}/templates/project.md +0 -0
- /package/{get-shit-done → ez-agents}/templates/research.md +0 -0
- /package/{get-shit-done → ez-agents}/templates/state.md +0 -0
- /package/{get-shit-done → ez-agents}/templates/summary-complex.md +0 -0
|
@@ -1,150 +1,150 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* GSD Audit Exec — Command execution with full audit logging
|
|
5
|
-
*
|
|
6
|
-
* Logs all command executions to audit file for security review:
|
|
7
|
-
* - Timestamp, command, arguments, context
|
|
8
|
-
* - Duration and result status
|
|
9
|
-
* - Error details if failed
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* const { auditExec } = require('./audit-exec.cjs');
|
|
13
|
-
* const result = await auditExec('git', ['status'], { context: 'my-module' });
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
const { execFile } = require('child_process');
|
|
17
|
-
const { promisify } = require('util');
|
|
18
|
-
const { appendFileSync, existsSync, mkdirSync } = require('fs');
|
|
19
|
-
const { join } = require('path');
|
|
20
|
-
const Logger = require('./logger.cjs');
|
|
21
|
-
const logger = new Logger();
|
|
22
|
-
|
|
23
|
-
const execFileAsync = promisify(execFile);
|
|
24
|
-
|
|
25
|
-
// Audit log file path
|
|
26
|
-
const AUDIT_DIR = join('.planning', 'logs');
|
|
27
|
-
const AUDIT_FILE = join(AUDIT_DIR, `audit-${new Date().toISOString().split('T')[0]}.jsonl`);
|
|
28
|
-
|
|
29
|
-
// Ensure audit directory exists
|
|
30
|
-
if (!existsSync(AUDIT_DIR)) {
|
|
31
|
-
mkdirSync(AUDIT_DIR, { recursive: true });
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Write audit log entry
|
|
36
|
-
* @param {Object} entry - Audit entry
|
|
37
|
-
*/
|
|
38
|
-
function writeAudit(entry) {
|
|
39
|
-
const line = JSON.stringify(entry) + '\n';
|
|
40
|
-
appendFileSync(AUDIT_FILE, line, 'utf-8');
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Execute command with full audit logging
|
|
45
|
-
* @param {string} cmd - Command to execute
|
|
46
|
-
* @param {string[]} args - Command arguments
|
|
47
|
-
* @param {Object} options - Execution options
|
|
48
|
-
* @param {string} options.context - Calling context (which module/function)
|
|
49
|
-
* @param {string} options.user - User identifier
|
|
50
|
-
* @returns {Promise<string>} - Command stdout
|
|
51
|
-
*/
|
|
52
|
-
async function auditExec(cmd, args = [], options = {}) {
|
|
53
|
-
const { context = 'unknown', user = 'system', timeout = 30000 } = options;
|
|
54
|
-
|
|
55
|
-
const entry = {
|
|
56
|
-
timestamp: new Date().toISOString(),
|
|
57
|
-
cmd,
|
|
58
|
-
args,
|
|
59
|
-
context,
|
|
60
|
-
user,
|
|
61
|
-
status: 'started'
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
// Log start
|
|
65
|
-
writeAudit(entry);
|
|
66
|
-
logger.info('Audit: command started', { cmd, args: args.join(' '), context });
|
|
67
|
-
|
|
68
|
-
const startTime = Date.now();
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
const result = await execFileAsync(cmd, args, {
|
|
72
|
-
timeout,
|
|
73
|
-
maxBuffer: 10 * 1024 * 1024
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const duration = Date.now() - startTime;
|
|
77
|
-
|
|
78
|
-
// Log success
|
|
79
|
-
const successEntry = {
|
|
80
|
-
...entry,
|
|
81
|
-
status: 'success',
|
|
82
|
-
duration,
|
|
83
|
-
stdout_length: result.stdout?.length || 0
|
|
84
|
-
};
|
|
85
|
-
writeAudit(successEntry);
|
|
86
|
-
|
|
87
|
-
logger.debug('Audit: command completed', { cmd, duration, context });
|
|
88
|
-
|
|
89
|
-
return result.stdout.trim();
|
|
90
|
-
} catch (err) {
|
|
91
|
-
const duration = Date.now() - startTime;
|
|
92
|
-
|
|
93
|
-
// Log failure
|
|
94
|
-
const errorEntry = {
|
|
95
|
-
...entry,
|
|
96
|
-
status: 'error',
|
|
97
|
-
duration,
|
|
98
|
-
error: err.message,
|
|
99
|
-
code: err.code,
|
|
100
|
-
signal: err.signal
|
|
101
|
-
};
|
|
102
|
-
writeAudit(errorEntry);
|
|
103
|
-
|
|
104
|
-
logger.error('Audit: command failed', { cmd, error: err.message, duration, context });
|
|
105
|
-
|
|
106
|
-
throw err;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Get today's audit log path
|
|
112
|
-
* @returns {string} - Audit file path
|
|
113
|
-
*/
|
|
114
|
-
function getAuditFilePath() {
|
|
115
|
-
return AUDIT_FILE;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Read audit log entries for a specific date
|
|
120
|
-
* @param {string} date - Date string (YYYY-MM-DD)
|
|
121
|
-
* @returns {Object[]} - Array of audit entries
|
|
122
|
-
*/
|
|
123
|
-
function readAuditLog(date = new Date().toISOString().split('T')[0]) {
|
|
124
|
-
const filePath = join(AUDIT_DIR, `audit-${date}.jsonl`);
|
|
125
|
-
|
|
126
|
-
if (!existsSync(filePath)) {
|
|
127
|
-
return [];
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const content = require('fs').readFileSync(filePath, 'utf-8');
|
|
131
|
-
return content.trim().split('\n').map(line => JSON.parse(line));
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Search audit log for specific command
|
|
136
|
-
* @param {string} cmdFilter - Command to filter by
|
|
137
|
-
* @param {string} date - Date string
|
|
138
|
-
* @returns {Object[]} - Matching entries
|
|
139
|
-
*/
|
|
140
|
-
function searchAuditLog(cmdFilter, date) {
|
|
141
|
-
const entries = readAuditLog(date);
|
|
142
|
-
return entries.filter(e => e.cmd === cmdFilter);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
module.exports = {
|
|
146
|
-
auditExec,
|
|
147
|
-
getAuditFilePath,
|
|
148
|
-
readAuditLog,
|
|
149
|
-
searchAuditLog
|
|
150
|
-
};
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GSD Audit Exec — Command execution with full audit logging
|
|
5
|
+
*
|
|
6
|
+
* Logs all command executions to audit file for security review:
|
|
7
|
+
* - Timestamp, command, arguments, context
|
|
8
|
+
* - Duration and result status
|
|
9
|
+
* - Error details if failed
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* const { auditExec } = require('./audit-exec.cjs');
|
|
13
|
+
* const result = await auditExec('git', ['status'], { context: 'my-module' });
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { execFile } = require('child_process');
|
|
17
|
+
const { promisify } = require('util');
|
|
18
|
+
const { appendFileSync, existsSync, mkdirSync } = require('fs');
|
|
19
|
+
const { join } = require('path');
|
|
20
|
+
const Logger = require('./logger.cjs');
|
|
21
|
+
const logger = new Logger();
|
|
22
|
+
|
|
23
|
+
const execFileAsync = promisify(execFile);
|
|
24
|
+
|
|
25
|
+
// Audit log file path
|
|
26
|
+
const AUDIT_DIR = join('.planning', 'logs');
|
|
27
|
+
const AUDIT_FILE = join(AUDIT_DIR, `audit-${new Date().toISOString().split('T')[0]}.jsonl`);
|
|
28
|
+
|
|
29
|
+
// Ensure audit directory exists
|
|
30
|
+
if (!existsSync(AUDIT_DIR)) {
|
|
31
|
+
mkdirSync(AUDIT_DIR, { recursive: true });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Write audit log entry
|
|
36
|
+
* @param {Object} entry - Audit entry
|
|
37
|
+
*/
|
|
38
|
+
function writeAudit(entry) {
|
|
39
|
+
const line = JSON.stringify(entry) + '\n';
|
|
40
|
+
appendFileSync(AUDIT_FILE, line, 'utf-8');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Execute command with full audit logging
|
|
45
|
+
* @param {string} cmd - Command to execute
|
|
46
|
+
* @param {string[]} args - Command arguments
|
|
47
|
+
* @param {Object} options - Execution options
|
|
48
|
+
* @param {string} options.context - Calling context (which module/function)
|
|
49
|
+
* @param {string} options.user - User identifier
|
|
50
|
+
* @returns {Promise<string>} - Command stdout
|
|
51
|
+
*/
|
|
52
|
+
async function auditExec(cmd, args = [], options = {}) {
|
|
53
|
+
const { context = 'unknown', user = 'system', timeout = 30000 } = options;
|
|
54
|
+
|
|
55
|
+
const entry = {
|
|
56
|
+
timestamp: new Date().toISOString(),
|
|
57
|
+
cmd,
|
|
58
|
+
args,
|
|
59
|
+
context,
|
|
60
|
+
user,
|
|
61
|
+
status: 'started'
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Log start
|
|
65
|
+
writeAudit(entry);
|
|
66
|
+
logger.info('Audit: command started', { cmd, args: args.join(' '), context });
|
|
67
|
+
|
|
68
|
+
const startTime = Date.now();
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const result = await execFileAsync(cmd, args, {
|
|
72
|
+
timeout,
|
|
73
|
+
maxBuffer: 10 * 1024 * 1024
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const duration = Date.now() - startTime;
|
|
77
|
+
|
|
78
|
+
// Log success
|
|
79
|
+
const successEntry = {
|
|
80
|
+
...entry,
|
|
81
|
+
status: 'success',
|
|
82
|
+
duration,
|
|
83
|
+
stdout_length: result.stdout?.length || 0
|
|
84
|
+
};
|
|
85
|
+
writeAudit(successEntry);
|
|
86
|
+
|
|
87
|
+
logger.debug('Audit: command completed', { cmd, duration, context });
|
|
88
|
+
|
|
89
|
+
return result.stdout.trim();
|
|
90
|
+
} catch (err) {
|
|
91
|
+
const duration = Date.now() - startTime;
|
|
92
|
+
|
|
93
|
+
// Log failure
|
|
94
|
+
const errorEntry = {
|
|
95
|
+
...entry,
|
|
96
|
+
status: 'error',
|
|
97
|
+
duration,
|
|
98
|
+
error: err.message,
|
|
99
|
+
code: err.code,
|
|
100
|
+
signal: err.signal
|
|
101
|
+
};
|
|
102
|
+
writeAudit(errorEntry);
|
|
103
|
+
|
|
104
|
+
logger.error('Audit: command failed', { cmd, error: err.message, duration, context });
|
|
105
|
+
|
|
106
|
+
throw err;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get today's audit log path
|
|
112
|
+
* @returns {string} - Audit file path
|
|
113
|
+
*/
|
|
114
|
+
function getAuditFilePath() {
|
|
115
|
+
return AUDIT_FILE;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Read audit log entries for a specific date
|
|
120
|
+
* @param {string} date - Date string (YYYY-MM-DD)
|
|
121
|
+
* @returns {Object[]} - Array of audit entries
|
|
122
|
+
*/
|
|
123
|
+
function readAuditLog(date = new Date().toISOString().split('T')[0]) {
|
|
124
|
+
const filePath = join(AUDIT_DIR, `audit-${date}.jsonl`);
|
|
125
|
+
|
|
126
|
+
if (!existsSync(filePath)) {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const content = require('fs').readFileSync(filePath, 'utf-8');
|
|
131
|
+
return content.trim().split('\n').map(line => JSON.parse(line));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Search audit log for specific command
|
|
136
|
+
* @param {string} cmdFilter - Command to filter by
|
|
137
|
+
* @param {string} date - Date string
|
|
138
|
+
* @returns {Object[]} - Matching entries
|
|
139
|
+
*/
|
|
140
|
+
function searchAuditLog(cmdFilter, date) {
|
|
141
|
+
const entries = readAuditLog(date);
|
|
142
|
+
return entries.filter(e => e.cmd === cmdFilter);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
module.exports = {
|
|
146
|
+
auditExec,
|
|
147
|
+
getAuditFilePath,
|
|
148
|
+
readAuditLog,
|
|
149
|
+
searchAuditLog
|
|
150
|
+
};
|
|
@@ -1,175 +1,175 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* GSD Auth — Secure credential storage using system keychain
|
|
5
|
-
*
|
|
6
|
-
* Stores API keys securely using:
|
|
7
|
-
* - keytar for system keychain (Windows Credential Manager, macOS Keychain, libsecret)
|
|
8
|
-
* - Fallback to encrypted file storage if keytar unavailable
|
|
9
|
-
*
|
|
10
|
-
* Usage:
|
|
11
|
-
* const { saveCredential, loadCredential } = require('./auth.cjs');
|
|
12
|
-
* await saveCredential('anthropic', 'sk-...');
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const Logger = require('./logger.cjs');
|
|
16
|
-
const logger = new Logger();
|
|
17
|
-
const path = require('path');
|
|
18
|
-
const fs = require('fs');
|
|
19
|
-
const os = require('os');
|
|
20
|
-
|
|
21
|
-
const SERVICE_NAME = '
|
|
22
|
-
|
|
23
|
-
// Provider account names
|
|
24
|
-
const PROVIDERS = {
|
|
25
|
-
ANTHROPIC: 'anthropic',
|
|
26
|
-
MOONSHOT: 'moonshot',
|
|
27
|
-
ALIBABA: 'alibaba',
|
|
28
|
-
OPENAI: 'openai'
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// Fallback storage path
|
|
32
|
-
const FALLBACK_FILE = path.join(os.homedir(), '.
|
|
33
|
-
|
|
34
|
-
// Try to load keytar
|
|
35
|
-
let keytar = null;
|
|
36
|
-
try {
|
|
37
|
-
keytar = require('keytar');
|
|
38
|
-
logger.info('keytar loaded — using system keychain');
|
|
39
|
-
} catch (err) {
|
|
40
|
-
logger.warn('keytar not available — using fallback storage');
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Save credential securely
|
|
45
|
-
* @param {string} provider - Provider name (anthropic, moonshot, etc.)
|
|
46
|
-
* @param {string} secret - API key or secret
|
|
47
|
-
* @returns {Promise<boolean>} - Success status
|
|
48
|
-
*/
|
|
49
|
-
async function saveCredential(provider, secret) {
|
|
50
|
-
try {
|
|
51
|
-
if (keytar) {
|
|
52
|
-
await keytar.setPassword(SERVICE_NAME, provider, secret);
|
|
53
|
-
logger.info('Credential saved to system keychain', { provider });
|
|
54
|
-
return true;
|
|
55
|
-
} else {
|
|
56
|
-
// Fallback: save to file
|
|
57
|
-
let credentials = {};
|
|
58
|
-
if (fs.existsSync(FALLBACK_FILE)) {
|
|
59
|
-
const content = fs.readFileSync(FALLBACK_FILE, 'utf-8');
|
|
60
|
-
try {
|
|
61
|
-
credentials = JSON.parse(content);
|
|
62
|
-
} catch (e) {
|
|
63
|
-
credentials = {};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
credentials[provider] = secret;
|
|
67
|
-
fs.writeFileSync(FALLBACK_FILE, JSON.stringify(credentials, null, 2), 'utf-8');
|
|
68
|
-
logger.warn('Credential saved to fallback file', { provider, path: FALLBACK_FILE });
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
} catch (err) {
|
|
72
|
-
logger.error('Failed to save credential', { provider, error: err.message });
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Load credential securely
|
|
79
|
-
* @param {string} provider - Provider name
|
|
80
|
-
* @returns {Promise<string|null>} - API key or null if not found
|
|
81
|
-
*/
|
|
82
|
-
async function loadCredential(provider) {
|
|
83
|
-
try {
|
|
84
|
-
if (keytar) {
|
|
85
|
-
const secret = await keytar.getPassword(SERVICE_NAME, provider);
|
|
86
|
-
if (secret) {
|
|
87
|
-
logger.debug('Credential loaded from keychain', { provider });
|
|
88
|
-
}
|
|
89
|
-
return secret || null;
|
|
90
|
-
} else {
|
|
91
|
-
// Fallback: load from file
|
|
92
|
-
if (fs.existsSync(FALLBACK_FILE)) {
|
|
93
|
-
const content = fs.readFileSync(FALLBACK_FILE, 'utf-8');
|
|
94
|
-
const credentials = JSON.parse(content);
|
|
95
|
-
return credentials[provider] || null;
|
|
96
|
-
}
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
} catch (err) {
|
|
100
|
-
logger.error('Failed to load credential', { provider, error: err.message });
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Delete credential
|
|
107
|
-
* @param {string} provider - Provider name
|
|
108
|
-
* @returns {Promise<boolean>} - Success status
|
|
109
|
-
*/
|
|
110
|
-
async function deleteCredential(provider) {
|
|
111
|
-
try {
|
|
112
|
-
if (keytar) {
|
|
113
|
-
await keytar.deletePassword(SERVICE_NAME, provider);
|
|
114
|
-
logger.info('Credential deleted from keychain', { provider });
|
|
115
|
-
return true;
|
|
116
|
-
} else {
|
|
117
|
-
// Fallback: remove from file
|
|
118
|
-
if (fs.existsSync(FALLBACK_FILE)) {
|
|
119
|
-
const content = fs.readFileSync(FALLBACK_FILE, 'utf-8');
|
|
120
|
-
const credentials = JSON.parse(content);
|
|
121
|
-
if (credentials[provider]) {
|
|
122
|
-
delete credentials[provider];
|
|
123
|
-
fs.writeFileSync(FALLBACK_FILE, JSON.stringify(credentials, null, 2), 'utf-8');
|
|
124
|
-
logger.info('Credential deleted from fallback file', { provider });
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
} catch (err) {
|
|
131
|
-
logger.error('Failed to delete credential', { provider, error: err.message });
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* List all stored providers
|
|
138
|
-
* @returns {Promise<string[]>} - Array of provider names
|
|
139
|
-
*/
|
|
140
|
-
async function listProviders() {
|
|
141
|
-
const stored = [];
|
|
142
|
-
|
|
143
|
-
if (keytar) {
|
|
144
|
-
for (const [, name] of Object.entries(PROVIDERS)) {
|
|
145
|
-
const cred = await keytar.getPassword(SERVICE_NAME, name);
|
|
146
|
-
if (cred) stored.push(name);
|
|
147
|
-
}
|
|
148
|
-
} else {
|
|
149
|
-
if (fs.existsSync(FALLBACK_FILE)) {
|
|
150
|
-
const content = fs.readFileSync(FALLBACK_FILE, 'utf-8');
|
|
151
|
-
const credentials = JSON.parse(content);
|
|
152
|
-
return Object.keys(credentials);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return stored;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Check if keytar is available
|
|
161
|
-
* @returns {boolean} - True if using system keychain
|
|
162
|
-
*/
|
|
163
|
-
function isKeychainAvailable() {
|
|
164
|
-
return keytar !== null;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
module.exports = {
|
|
168
|
-
saveCredential,
|
|
169
|
-
loadCredential,
|
|
170
|
-
deleteCredential,
|
|
171
|
-
listProviders,
|
|
172
|
-
isKeychainAvailable,
|
|
173
|
-
PROVIDERS,
|
|
174
|
-
SERVICE_NAME
|
|
175
|
-
};
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GSD Auth — Secure credential storage using system keychain
|
|
5
|
+
*
|
|
6
|
+
* Stores API keys securely using:
|
|
7
|
+
* - keytar for system keychain (Windows Credential Manager, macOS Keychain, libsecret)
|
|
8
|
+
* - Fallback to encrypted file storage if keytar unavailable
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* const { saveCredential, loadCredential } = require('./auth.cjs');
|
|
12
|
+
* await saveCredential('anthropic', 'sk-...');
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const Logger = require('./logger.cjs');
|
|
16
|
+
const logger = new Logger();
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const os = require('os');
|
|
20
|
+
|
|
21
|
+
const SERVICE_NAME = 'ez-agents';
|
|
22
|
+
|
|
23
|
+
// Provider account names
|
|
24
|
+
const PROVIDERS = {
|
|
25
|
+
ANTHROPIC: 'anthropic',
|
|
26
|
+
MOONSHOT: 'moonshot',
|
|
27
|
+
ALIBABA: 'alibaba',
|
|
28
|
+
OPENAI: 'openai'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Fallback storage path
|
|
32
|
+
const FALLBACK_FILE = path.join(os.homedir(), '.ez-credentials.json');
|
|
33
|
+
|
|
34
|
+
// Try to load keytar
|
|
35
|
+
let keytar = null;
|
|
36
|
+
try {
|
|
37
|
+
keytar = require('keytar');
|
|
38
|
+
logger.info('keytar loaded — using system keychain');
|
|
39
|
+
} catch (err) {
|
|
40
|
+
logger.warn('keytar not available — using fallback storage');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Save credential securely
|
|
45
|
+
* @param {string} provider - Provider name (anthropic, moonshot, etc.)
|
|
46
|
+
* @param {string} secret - API key or secret
|
|
47
|
+
* @returns {Promise<boolean>} - Success status
|
|
48
|
+
*/
|
|
49
|
+
async function saveCredential(provider, secret) {
|
|
50
|
+
try {
|
|
51
|
+
if (keytar) {
|
|
52
|
+
await keytar.setPassword(SERVICE_NAME, provider, secret);
|
|
53
|
+
logger.info('Credential saved to system keychain', { provider });
|
|
54
|
+
return true;
|
|
55
|
+
} else {
|
|
56
|
+
// Fallback: save to file
|
|
57
|
+
let credentials = {};
|
|
58
|
+
if (fs.existsSync(FALLBACK_FILE)) {
|
|
59
|
+
const content = fs.readFileSync(FALLBACK_FILE, 'utf-8');
|
|
60
|
+
try {
|
|
61
|
+
credentials = JSON.parse(content);
|
|
62
|
+
} catch (e) {
|
|
63
|
+
credentials = {};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
credentials[provider] = secret;
|
|
67
|
+
fs.writeFileSync(FALLBACK_FILE, JSON.stringify(credentials, null, 2), 'utf-8');
|
|
68
|
+
logger.warn('Credential saved to fallback file', { provider, path: FALLBACK_FILE });
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
} catch (err) {
|
|
72
|
+
logger.error('Failed to save credential', { provider, error: err.message });
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Load credential securely
|
|
79
|
+
* @param {string} provider - Provider name
|
|
80
|
+
* @returns {Promise<string|null>} - API key or null if not found
|
|
81
|
+
*/
|
|
82
|
+
async function loadCredential(provider) {
|
|
83
|
+
try {
|
|
84
|
+
if (keytar) {
|
|
85
|
+
const secret = await keytar.getPassword(SERVICE_NAME, provider);
|
|
86
|
+
if (secret) {
|
|
87
|
+
logger.debug('Credential loaded from keychain', { provider });
|
|
88
|
+
}
|
|
89
|
+
return secret || null;
|
|
90
|
+
} else {
|
|
91
|
+
// Fallback: load from file
|
|
92
|
+
if (fs.existsSync(FALLBACK_FILE)) {
|
|
93
|
+
const content = fs.readFileSync(FALLBACK_FILE, 'utf-8');
|
|
94
|
+
const credentials = JSON.parse(content);
|
|
95
|
+
return credentials[provider] || null;
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
} catch (err) {
|
|
100
|
+
logger.error('Failed to load credential', { provider, error: err.message });
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Delete credential
|
|
107
|
+
* @param {string} provider - Provider name
|
|
108
|
+
* @returns {Promise<boolean>} - Success status
|
|
109
|
+
*/
|
|
110
|
+
async function deleteCredential(provider) {
|
|
111
|
+
try {
|
|
112
|
+
if (keytar) {
|
|
113
|
+
await keytar.deletePassword(SERVICE_NAME, provider);
|
|
114
|
+
logger.info('Credential deleted from keychain', { provider });
|
|
115
|
+
return true;
|
|
116
|
+
} else {
|
|
117
|
+
// Fallback: remove from file
|
|
118
|
+
if (fs.existsSync(FALLBACK_FILE)) {
|
|
119
|
+
const content = fs.readFileSync(FALLBACK_FILE, 'utf-8');
|
|
120
|
+
const credentials = JSON.parse(content);
|
|
121
|
+
if (credentials[provider]) {
|
|
122
|
+
delete credentials[provider];
|
|
123
|
+
fs.writeFileSync(FALLBACK_FILE, JSON.stringify(credentials, null, 2), 'utf-8');
|
|
124
|
+
logger.info('Credential deleted from fallback file', { provider });
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
} catch (err) {
|
|
131
|
+
logger.error('Failed to delete credential', { provider, error: err.message });
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* List all stored providers
|
|
138
|
+
* @returns {Promise<string[]>} - Array of provider names
|
|
139
|
+
*/
|
|
140
|
+
async function listProviders() {
|
|
141
|
+
const stored = [];
|
|
142
|
+
|
|
143
|
+
if (keytar) {
|
|
144
|
+
for (const [, name] of Object.entries(PROVIDERS)) {
|
|
145
|
+
const cred = await keytar.getPassword(SERVICE_NAME, name);
|
|
146
|
+
if (cred) stored.push(name);
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
if (fs.existsSync(FALLBACK_FILE)) {
|
|
150
|
+
const content = fs.readFileSync(FALLBACK_FILE, 'utf-8');
|
|
151
|
+
const credentials = JSON.parse(content);
|
|
152
|
+
return Object.keys(credentials);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return stored;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Check if keytar is available
|
|
161
|
+
* @returns {boolean} - True if using system keychain
|
|
162
|
+
*/
|
|
163
|
+
function isKeychainAvailable() {
|
|
164
|
+
return keytar !== null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
module.exports = {
|
|
168
|
+
saveCredential,
|
|
169
|
+
loadCredential,
|
|
170
|
+
deleteCredential,
|
|
171
|
+
listProviders,
|
|
172
|
+
isKeychainAvailable,
|
|
173
|
+
PROVIDERS,
|
|
174
|
+
SERVICE_NAME
|
|
175
|
+
};
|