@ia-qa/self-healing 1.18.0 → 1.19.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 +98 -1
- package/TUTORIAL.md +58 -0
- package/dist/ai/models.d.ts +12 -0
- package/dist/ai/models.js +33 -1
- package/dist/ai/models.js.map +1 -1
- package/dist/ai/resolver.d.ts +29 -6
- package/dist/ai/resolver.js +42 -7
- package/dist/ai/resolver.js.map +1 -1
- package/dist/cli/args.js +2 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/fix.js +9 -2
- package/dist/cli/fix.js.map +1 -1
- package/dist/cli/index.js +47 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/map.d.ts +15 -4
- package/dist/cli/map.js +66 -13
- package/dist/cli/map.js.map +1 -1
- package/dist/cli/secret.d.ts +1 -0
- package/dist/cli/secret.js +172 -0
- package/dist/cli/secret.js.map +1 -0
- package/dist/cli/session.d.ts +24 -0
- package/dist/cli/session.js +119 -0
- package/dist/cli/session.js.map +1 -0
- package/dist/cli/skill.d.ts +37 -14
- package/dist/cli/skill.js +36 -30
- package/dist/cli/skill.js.map +1 -1
- package/dist/config.d.ts +23 -2
- package/dist/config.js +50 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/secretStore.d.ts +49 -0
- package/dist/secretStore.js +244 -0
- package/dist/secretStore.js.map +1 -0
- package/dist/sessionCheck.d.ts +96 -0
- package/dist/sessionCheck.js +182 -0
- package/dist/sessionCheck.js.map +1 -0
- package/dist/unrepaired.d.ts +73 -0
- package/dist/unrepaired.js +0 -0
- package/dist/unrepaired.js.map +1 -0
- package/dist/volatile.d.ts +8 -0
- package/dist/volatile.js +24 -7
- package/dist/volatile.js.map +1 -1
- package/dist/volatileBlindSpot.d.ts +67 -0
- package/dist/volatileBlindSpot.js +0 -0
- package/dist/volatileBlindSpot.js.map +1 -0
- package/package.json +1 -1
- package/skills/ia-qa-heal/SKILL.md +29 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runSecret(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runSecret = runSecret;
|
|
37
|
+
const readline = __importStar(require("readline"));
|
|
38
|
+
const secretStore_1 = require("../secretStore");
|
|
39
|
+
/**
|
|
40
|
+
* `ia-qa-heal secret` — put a value in the OS store, so it stops being ambient.
|
|
41
|
+
*
|
|
42
|
+
* There is deliberately **no `get`**. A verb that prints a secret is a verb whose output ends
|
|
43
|
+
* up in a scrollback, a log, or an agent's transcript — and this whole feature exists to keep
|
|
44
|
+
* the value out of exactly those. `check` answers the only question worth asking from the
|
|
45
|
+
* outside: does it resolve, yes or no.
|
|
46
|
+
*
|
|
47
|
+
* `set` refuses without a TTY, for the same reason `login` does: it waits for a person. That
|
|
48
|
+
* refusal is aimed squarely at the agent driving the CLI — it must hand this step to the
|
|
49
|
+
* human, who types the value into their own terminal.
|
|
50
|
+
*/
|
|
51
|
+
const isTty = () => Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
52
|
+
/** Read one line with the terminal echo off, so the value is not left on screen. */
|
|
53
|
+
function askHidden(prompt) {
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
56
|
+
const input = process.stdin;
|
|
57
|
+
const onData = () => {
|
|
58
|
+
// Repaint the prompt with nothing after it: readline echoes each keystroke otherwise.
|
|
59
|
+
readline.clearLine(process.stdout, 0);
|
|
60
|
+
readline.cursorTo(process.stdout, 0);
|
|
61
|
+
process.stdout.write(prompt);
|
|
62
|
+
};
|
|
63
|
+
process.stdout.write(prompt);
|
|
64
|
+
input.on('data', onData);
|
|
65
|
+
rl.question('', (answer) => {
|
|
66
|
+
input.removeListener('data', onData);
|
|
67
|
+
rl.close();
|
|
68
|
+
process.stdout.write('\n');
|
|
69
|
+
resolve(answer);
|
|
70
|
+
});
|
|
71
|
+
rl.on('error', reject);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function usage() {
|
|
75
|
+
console.error(`❌ \`ia-qa-heal secret\` needs a subcommand.\n\n` +
|
|
76
|
+
` ia-qa-heal secret set <NAME> store a value in this machine's credential store\n` +
|
|
77
|
+
` ia-qa-heal secret list the names it holds — never the values\n` +
|
|
78
|
+
` ia-qa-heal secret check <NAME> does it resolve? yes or no, never the value\n` +
|
|
79
|
+
` ia-qa-heal secret rm <NAME> forget it\n\n` +
|
|
80
|
+
` This machine: ${(0, secretStore_1.storeDescription)()}.\n` +
|
|
81
|
+
` Then reference it: { "source": "keychain", "key": "<NAME>" }`);
|
|
82
|
+
process.exitCode = 2;
|
|
83
|
+
}
|
|
84
|
+
async function runSecret(args) {
|
|
85
|
+
const [sub, name] = args.filter((a) => !a.startsWith('-'));
|
|
86
|
+
const json = args.includes('--json');
|
|
87
|
+
if (!sub)
|
|
88
|
+
return usage();
|
|
89
|
+
if (sub === 'list') {
|
|
90
|
+
const names = (0, secretStore_1.listSecrets)();
|
|
91
|
+
if (json) {
|
|
92
|
+
console.log(JSON.stringify({ schema: 'ia-qa-heal/secrets@1', store: (0, secretStore_1.storeDescription)(), names }, null, 2));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
console.log(`\n🔐 ${(0, secretStore_1.storeDescription)()}\n`);
|
|
96
|
+
if (names.length === 0)
|
|
97
|
+
console.log(' Nothing stored yet → ia-qa-heal secret set <NAME>\n');
|
|
98
|
+
else {
|
|
99
|
+
for (const n of names)
|
|
100
|
+
console.log(` ${n}`);
|
|
101
|
+
console.log('\n Names only. This command cannot print a value, by design.\n');
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (!name)
|
|
106
|
+
return usage();
|
|
107
|
+
if (sub === 'check') {
|
|
108
|
+
const got = (0, secretStore_1.readSecret)(name);
|
|
109
|
+
// Never the value, and never its length either: a length narrows a guess.
|
|
110
|
+
if (json)
|
|
111
|
+
console.log(JSON.stringify({ name, resolves: got.ok, ...(got.ok ? {} : { reason: got.reason }) }, null, 2));
|
|
112
|
+
else if (got.ok)
|
|
113
|
+
console.log(`\n✅ "${name}" resolves from ${(0, secretStore_1.storeDescription)()}.\n`);
|
|
114
|
+
else
|
|
115
|
+
console.error(`\n❌ "${name}" does not resolve — ${got.reason}\n`);
|
|
116
|
+
process.exitCode = got.ok ? 0 : 1;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (sub === 'rm') {
|
|
120
|
+
const r = (0, secretStore_1.removeSecret)(name);
|
|
121
|
+
if (!r.ok) {
|
|
122
|
+
console.error(`❌ ${r.reason}`);
|
|
123
|
+
process.exitCode = 2;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
console.log(`\n🗑 "${name}" removed from ${(0, secretStore_1.storeDescription)()}.\n`);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (sub !== 'set')
|
|
130
|
+
return usage();
|
|
131
|
+
if ((0, secretStore_1.storeKind)() === 'unsupported') {
|
|
132
|
+
console.error(`❌ ${(0, secretStore_1.storeDescription)()}.\n Use { "source": "env", "key": "${name}" } and export the value in your shell.`);
|
|
133
|
+
process.exitCode = 2;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (!isTty()) {
|
|
137
|
+
console.error(`❌ \`ia-qa-heal secret set\` needs a terminal: it asks for the value and must not read it\n` +
|
|
138
|
+
` from an argument or a pipe — both end up in a shell history, a process list or a log.\n` +
|
|
139
|
+
` A human runs this in their own shell. An agent cannot do it for them.\n` +
|
|
140
|
+
` For CI, where no person and no store exist: { "source": "env", "key": "${name}" }.`);
|
|
141
|
+
process.exitCode = 2;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
console.log(`\n🔐 Storing "${name}" in ${(0, secretStore_1.storeDescription)()}.`);
|
|
145
|
+
console.log(' The value is not echoed, not logged, and never written to a config file.');
|
|
146
|
+
const value = (await askHidden(` Value for ${name}: `)).trim();
|
|
147
|
+
if (!value) {
|
|
148
|
+
console.error('❌ Nothing entered — nothing stored.');
|
|
149
|
+
process.exitCode = 2;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const r = (0, secretStore_1.writeSecret)(name, value);
|
|
153
|
+
if (!r.ok) {
|
|
154
|
+
console.error(`❌ ${r.reason}`);
|
|
155
|
+
process.exitCode = 2;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// Proven, not assumed: written and read back before it is called stored. The whole package
|
|
159
|
+
// refuses to print "saved ✅" on anything it has not verified.
|
|
160
|
+
const back = (0, secretStore_1.readSecret)(name);
|
|
161
|
+
if (!back.ok || back.value !== value) {
|
|
162
|
+
console.error(`❌ Written, but reading it back did not return the same value — so it is NOT usable.\n` +
|
|
163
|
+
` ${back.ok ? 'The store returned something else.' : back.reason}`);
|
|
164
|
+
process.exitCode = 2;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
console.log(`\n✅ Stored and read back. Reference it without the value:\n`);
|
|
168
|
+
console.log(` { "source": "keychain", "key": "${name}" }\n`);
|
|
169
|
+
console.log(` It is no longer in your environment, so nothing else in this session inherits it.`);
|
|
170
|
+
console.log(` An agent can still ask the OS for it — that is now a visible act, not an ambient one.\n`);
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=secret.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret.js","sourceRoot":"","sources":["../../src/cli/secret.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type SessionFacts, type SessionVerdict } from '../sessionCheck';
|
|
2
|
+
/**
|
|
3
|
+
* `ia-qa-heal session --check` — does this session still open the app, and if not, why?
|
|
4
|
+
*
|
|
5
|
+
* Every other verb here answers a question about the *app*. This one answers a question
|
|
6
|
+
* about the tool's own credential, which until now was only ever reported as a side effect
|
|
7
|
+
* of something expensive failing: `map` walked into a wall, printed three possible causes,
|
|
8
|
+
* and offered no way to choose between them. The field wrote its own script.
|
|
9
|
+
*
|
|
10
|
+
* Read-only, and cheap by design: the offline causes (expired, wrong host, empty) are
|
|
11
|
+
* conclusive from the file, so the browser opens only when the file looks fine and the
|
|
12
|
+
* question is what the *server* thinks. `--offline` skips it entirely.
|
|
13
|
+
*
|
|
14
|
+
* Exit codes follow the package's convention: 0 it holds, 1 it does not, 2 could not tell.
|
|
15
|
+
* That last one is why "could not open the app" is never folded into "the session is dead" —
|
|
16
|
+
* a network error is a fact about the network.
|
|
17
|
+
*/
|
|
18
|
+
export interface SessionCheck {
|
|
19
|
+
file: string;
|
|
20
|
+
source: string;
|
|
21
|
+
facts: SessionFacts;
|
|
22
|
+
verdict: SessionVerdict;
|
|
23
|
+
}
|
|
24
|
+
export declare function runSessionCheck(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runSessionCheck = runSessionCheck;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const config_1 = require("../config");
|
|
39
|
+
const launcher_1 = require("../launcher");
|
|
40
|
+
const login_1 = require("./login");
|
|
41
|
+
const sessionCheck_1 = require("../sessionCheck");
|
|
42
|
+
function render(check) {
|
|
43
|
+
const { facts, verdict } = check;
|
|
44
|
+
const mark = verdict.holds ? '✅' : verdict.cause === 'unknown' ? '⚠' : '❌';
|
|
45
|
+
const lines = [
|
|
46
|
+
`\n🔑 ${check.file}`,
|
|
47
|
+
` ${facts.cookies} cookie${facts.cookies === 1 ? '' : 's'}, ${facts.origins} origin${facts.origins === 1 ? '' : 's'} with local storage${facts.domains.length > 0 ? ` · ${facts.domains.join(', ')}` : ''}`,
|
|
48
|
+
];
|
|
49
|
+
// Only when it says something. A session cookie with no stated expiry is the ordinary
|
|
50
|
+
// case and printing "expires: never stated" on every run teaches nothing.
|
|
51
|
+
if (facts.expiresInSeconds !== null) {
|
|
52
|
+
lines.push(facts.expiresInSeconds >= 0
|
|
53
|
+
? ` First cookie expires in ${(0, sessionCheck_1.describeAge)(facts.expiresInSeconds)}`
|
|
54
|
+
: ` Expired ${(0, sessionCheck_1.describeAge)(facts.expiresInSeconds)} ago`);
|
|
55
|
+
}
|
|
56
|
+
lines.push(`\n${mark} ${verdict.headline}`);
|
|
57
|
+
for (const line of verdict.remedy)
|
|
58
|
+
lines.push(` ${line}`);
|
|
59
|
+
return lines;
|
|
60
|
+
}
|
|
61
|
+
async function runSessionCheck(args) {
|
|
62
|
+
const json = args.includes('--json');
|
|
63
|
+
const offline = args.includes('--offline');
|
|
64
|
+
const urlIdx = args.indexOf('--url');
|
|
65
|
+
const wantedUrl = urlIdx !== -1 ? args[urlIdx + 1] : undefined;
|
|
66
|
+
const sessionIdx = args.indexOf('--session');
|
|
67
|
+
const wantedSession = sessionIdx !== -1 ? args[sessionIdx + 1] : undefined;
|
|
68
|
+
const config = (0, config_1.loadConfig)();
|
|
69
|
+
const choice = (0, config_1.resolveSession)(config, wantedSession);
|
|
70
|
+
if (!choice) {
|
|
71
|
+
// Not a failure: a project with no session is the ordinary case for a public app or one
|
|
72
|
+
// using `config.auth`. Saying "no session is broken" would be a wrong verdict.
|
|
73
|
+
const message = `No session file to check.\n` +
|
|
74
|
+
` This project has none, which is correct for a public app or one using "auth" in\n` +
|
|
75
|
+
` config.json. A person makes one with: ia-qa-heal login`;
|
|
76
|
+
if (json)
|
|
77
|
+
console.log(JSON.stringify({ schema: 'ia-qa-heal/session@1', present: false, message }, null, 2));
|
|
78
|
+
else
|
|
79
|
+
console.log(`\nℹ️ ${message}`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
let state;
|
|
83
|
+
try {
|
|
84
|
+
state = JSON.parse(fs.readFileSync(choice.file, 'utf8'));
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
console.error(`❌ ${choice.file} could not be read: ${err instanceof Error ? err.message : String(err)}`);
|
|
88
|
+
process.exitCode = 2;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const target = wantedUrl ?? config.baseUrl;
|
|
92
|
+
const facts = (0, sessionCheck_1.readFacts)(state, target);
|
|
93
|
+
// The offline causes are conclusive, so the browser is not opened to re-confirm an expiry
|
|
94
|
+
// date — the cheapest honest answer, not a degraded one.
|
|
95
|
+
const offlineVerdict = (0, sessionCheck_1.judgeSession)(facts);
|
|
96
|
+
const decidedOffline = offlineVerdict.cause !== 'unknown';
|
|
97
|
+
let verdict = offlineVerdict;
|
|
98
|
+
if (!decidedOffline && !offline) {
|
|
99
|
+
const { browser } = await (0, launcher_1.launchBrowser)();
|
|
100
|
+
try {
|
|
101
|
+
const replay = await (0, login_1.replaySession)(browser, state, target);
|
|
102
|
+
verdict = (0, sessionCheck_1.judgeSession)(facts, replay);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
await browser.close();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const check = { file: choice.file, source: choice.source, facts, verdict };
|
|
109
|
+
if (json) {
|
|
110
|
+
console.log(JSON.stringify({ schema: 'ia-qa-heal/session@1', present: true, ...check }, null, 2));
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
console.log(render(check).join('\n'));
|
|
114
|
+
}
|
|
115
|
+
// `unknown` is not `false`: the tool could not answer, and a caller must be able to tell
|
|
116
|
+
// that from "the session is dead" — the 0/1/2 split every verb here uses.
|
|
117
|
+
process.exitCode = verdict.holds ? 0 : verdict.cause === 'unknown' ? 2 : 1;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/cli/session.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/skill.d.ts
CHANGED
|
@@ -13,21 +13,44 @@
|
|
|
13
13
|
* overreach as `discover --apply` doing more than it says.
|
|
14
14
|
*/
|
|
15
15
|
export declare const SKILL_NAME = "ia-qa-heal";
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
/**
|
|
17
|
+
* What a skill verb needs to know about its own package.
|
|
18
|
+
*
|
|
19
|
+
* A factory, because `@ia-qa/qa-discovery` and `@ia-qa/pal` need the identical verb: a third
|
|
20
|
+
* copy of a file whose whole point is "agents read `.claude/skills/`, never `node_modules`"
|
|
21
|
+
* would be the doctrine drifting in the very place that states it. `packageRoot` is passed
|
|
22
|
+
* in rather than derived — `__dirname` here is heal's own `dist/`, not the caller's.
|
|
23
|
+
*/
|
|
24
|
+
export interface SkillVerbSpec {
|
|
25
|
+
/** Directory name under `.claude/skills/`, and the skill's identity. */
|
|
26
|
+
name: string;
|
|
27
|
+
/** The binary a user types. Messages only. */
|
|
28
|
+
cli: string;
|
|
29
|
+
/** The calling package's root: `path.join(__dirname, '..', '..')` from its own `cli/`. */
|
|
30
|
+
packageRoot: string;
|
|
31
|
+
}
|
|
32
|
+
export interface SkillVerb {
|
|
33
|
+
skillSourcePath(): string;
|
|
34
|
+
readSkill(): string;
|
|
35
|
+
skillTargetDir(opts: {
|
|
36
|
+
user?: boolean;
|
|
37
|
+
dir?: string;
|
|
38
|
+
}): string;
|
|
39
|
+
runSkill(args: string[]): Promise<void>;
|
|
22
40
|
}
|
|
23
41
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
42
|
+
* Build the verb for one package.
|
|
43
|
+
*
|
|
44
|
+
* `dist/cli/` and `src/cli/` are both two levels under a package root, so the caller's
|
|
45
|
+
* `path.join(__dirname, '..', '..')` resolves whether it runs from the published build or
|
|
46
|
+
* from the TypeScript sources under test.
|
|
27
47
|
*/
|
|
28
|
-
export declare function
|
|
29
|
-
export declare
|
|
48
|
+
export declare function createSkillVerb(spec: SkillVerbSpec): SkillVerb;
|
|
49
|
+
export declare const skillSourcePath: () => string;
|
|
50
|
+
export declare const readSkill: () => string;
|
|
30
51
|
/** Target is a project (or home) root; the skill always lands in `<root>/.claude/skills/<name>/`. */
|
|
31
|
-
export declare
|
|
32
|
-
|
|
33
|
-
|
|
52
|
+
export declare const skillTargetDir: (opts: {
|
|
53
|
+
user?: boolean;
|
|
54
|
+
dir?: string;
|
|
55
|
+
}) => string;
|
|
56
|
+
export declare const runSkill: (args: string[]) => Promise<void>;
|
package/dist/cli/skill.js
CHANGED
|
@@ -33,11 +33,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.SKILL_NAME = void 0;
|
|
37
|
-
exports.
|
|
38
|
-
exports.readSkill = readSkill;
|
|
39
|
-
exports.skillTargetDir = skillTargetDir;
|
|
40
|
-
exports.runSkill = runSkill;
|
|
36
|
+
exports.runSkill = exports.skillTargetDir = exports.readSkill = exports.skillSourcePath = exports.SKILL_NAME = void 0;
|
|
37
|
+
exports.createSkillVerb = createSkillVerb;
|
|
41
38
|
const fs = __importStar(require("fs"));
|
|
42
39
|
const os = __importStar(require("os"));
|
|
43
40
|
const path = __importStar(require("path"));
|
|
@@ -57,26 +54,34 @@ const path = __importStar(require("path"));
|
|
|
57
54
|
*/
|
|
58
55
|
exports.SKILL_NAME = 'ia-qa-heal';
|
|
59
56
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
57
|
+
* Build the verb for one package.
|
|
58
|
+
*
|
|
59
|
+
* `dist/cli/` and `src/cli/` are both two levels under a package root, so the caller's
|
|
60
|
+
* `path.join(__dirname, '..', '..')` resolves whether it runs from the published build or
|
|
61
|
+
* from the TypeScript sources under test.
|
|
63
62
|
*/
|
|
64
|
-
function
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
63
|
+
function createSkillVerb(spec) {
|
|
64
|
+
const skillSourcePath = () => path.join(spec.packageRoot, 'skills', spec.name, 'SKILL.md');
|
|
65
|
+
const readSkill = () => {
|
|
66
|
+
const src = skillSourcePath();
|
|
67
|
+
if (!fs.existsSync(src)) {
|
|
68
|
+
throw new Error(`Skill source missing at ${src}.\n` +
|
|
69
|
+
`The installed package is incomplete — reinstall it. (A tarball that dropped "skills" ` +
|
|
70
|
+
`from package.json "files" ships exactly like this.)`);
|
|
71
|
+
}
|
|
72
|
+
return fs.readFileSync(src, 'utf8');
|
|
73
|
+
};
|
|
74
|
+
const skillTargetDir = (opts) => {
|
|
75
|
+
const root = opts.dir !== undefined ? opts.dir : opts.user ? os.homedir() : process.cwd();
|
|
76
|
+
return path.join(root, '.claude', 'skills', spec.name);
|
|
77
|
+
};
|
|
78
|
+
return { skillSourcePath, readSkill, skillTargetDir, runSkill: (args) => run(spec, { skillSourcePath, readSkill, skillTargetDir }, args) };
|
|
74
79
|
}
|
|
80
|
+
const healVerb = createSkillVerb({ name: exports.SKILL_NAME, cli: 'ia-qa-heal', packageRoot: path.join(__dirname, '..', '..') });
|
|
81
|
+
exports.skillSourcePath = healVerb.skillSourcePath;
|
|
82
|
+
exports.readSkill = healVerb.readSkill;
|
|
75
83
|
/** Target is a project (or home) root; the skill always lands in `<root>/.claude/skills/<name>/`. */
|
|
76
|
-
|
|
77
|
-
const root = opts.dir !== undefined ? opts.dir : opts.user ? os.homedir() : process.cwd();
|
|
78
|
-
return path.join(root, '.claude', 'skills', exports.SKILL_NAME);
|
|
79
|
-
}
|
|
84
|
+
exports.skillTargetDir = healVerb.skillTargetDir;
|
|
80
85
|
function parseArgs(args) {
|
|
81
86
|
const opts = { install: false, user: false, print: false, force: false };
|
|
82
87
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -106,23 +111,23 @@ function parseArgs(args) {
|
|
|
106
111
|
}
|
|
107
112
|
return opts;
|
|
108
113
|
}
|
|
109
|
-
async function
|
|
114
|
+
async function run(spec, io, args) {
|
|
110
115
|
const opts = parseArgs(args);
|
|
111
|
-
const content = readSkill();
|
|
116
|
+
const content = io.readSkill();
|
|
112
117
|
if (opts.print) {
|
|
113
118
|
process.stdout.write(content);
|
|
114
119
|
return;
|
|
115
120
|
}
|
|
116
|
-
const dir = skillTargetDir(opts);
|
|
121
|
+
const dir = io.skillTargetDir(opts);
|
|
117
122
|
const file = path.join(dir, 'SKILL.md');
|
|
118
123
|
const exists = fs.existsSync(file);
|
|
119
124
|
const current = exists ? fs.readFileSync(file, 'utf8') : null;
|
|
120
125
|
if (!opts.install) {
|
|
121
|
-
console.log(
|
|
122
|
-
console.log(` source: ${skillSourcePath()}`);
|
|
126
|
+
console.log(`\n🧠 ${spec.cli} skill — agent instructions for this CLI\n`);
|
|
127
|
+
console.log(` source: ${io.skillSourcePath()}`);
|
|
123
128
|
console.log(` target: ${file}`);
|
|
124
129
|
if (!exists)
|
|
125
|
-
console.log(
|
|
130
|
+
console.log(` status: not installed → ${spec.cli} skill --install`);
|
|
126
131
|
else if (current === content)
|
|
127
132
|
console.log(' status: installed, up to date');
|
|
128
133
|
else
|
|
@@ -138,12 +143,13 @@ async function runSkill(args) {
|
|
|
138
143
|
if (exists && !opts.force) {
|
|
139
144
|
throw new Error(`${file} already exists and differs from the version in this package.\n` +
|
|
140
145
|
`It may have been edited on purpose, so this stops rather than overwrite it.\n` +
|
|
141
|
-
`Compare first —
|
|
142
|
-
`Then overwrite —
|
|
146
|
+
`Compare first — ${spec.cli} skill --print > SKILL.new.md\n` +
|
|
147
|
+
`Then overwrite — ${spec.cli} skill --install --force`);
|
|
143
148
|
}
|
|
144
149
|
console.log(`\n📝 Writing ${file}`);
|
|
145
150
|
fs.mkdirSync(dir, { recursive: true });
|
|
146
151
|
fs.writeFileSync(file, content, 'utf8');
|
|
147
152
|
console.log(`✅ ${exists ? 'Updated' : 'Installed'} — start a new agent session to load it.\n`);
|
|
148
153
|
}
|
|
154
|
+
exports.runSkill = healVerb.runSkill;
|
|
149
155
|
//# sourceMappingURL=skill.js.map
|
package/dist/cli/skill.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill.js","sourceRoot":"","sources":["../../src/cli/skill.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"skill.js","sourceRoot":"","sources":["../../src/cli/skill.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AiProvider } from './ai/resolver';
|
|
1
2
|
/**
|
|
2
3
|
* Configuration & secret resolution for @ia-qa/self-healing.
|
|
3
4
|
*
|
|
@@ -6,7 +7,7 @@
|
|
|
6
7
|
* resolved at runtime — from `process.env` or from SSM Parameter Store using
|
|
7
8
|
* the developer's local AWS credential chain — and are never written to disk.
|
|
8
9
|
*/
|
|
9
|
-
export type SecretSource = 'env' | 'aws-ssm';
|
|
10
|
+
export type SecretSource = 'env' | 'aws-ssm' | 'keychain';
|
|
10
11
|
export interface SecretRef {
|
|
11
12
|
source: SecretSource;
|
|
12
13
|
/** `env`: the environment variable name. `aws-ssm`: the parameter name (e.g. `/staging/app/password`). */
|
|
@@ -67,8 +68,17 @@ export interface PageTarget {
|
|
|
67
68
|
* `model` is pinned by the user (determinism); the call runs at temperature 0.
|
|
68
69
|
*/
|
|
69
70
|
export interface AiConfig {
|
|
70
|
-
|
|
71
|
+
/** The four the registry knows. `openai-compatible` is a shape, not a vendor — see `baseUrl`. */
|
|
72
|
+
provider: AiProvider;
|
|
71
73
|
model: string;
|
|
74
|
+
/**
|
|
75
|
+
* Required for `openai-compatible`, meaningless otherwise: where that endpoint lives.
|
|
76
|
+
*
|
|
77
|
+
* `https://api.deepseek.com/v1`, `https://api.groq.com/openai/v1`,
|
|
78
|
+
* `https://openrouter.ai/api/v1`, or `http://localhost:11434/v1` for a model running on
|
|
79
|
+
* this machine — in which case nothing leaves it at all.
|
|
80
|
+
*/
|
|
81
|
+
baseUrl?: string;
|
|
72
82
|
apiKey: SecretRef;
|
|
73
83
|
/** Reject a suggestion below this confidence. Default 0.7. */
|
|
74
84
|
minConfidence?: number;
|
|
@@ -364,6 +374,17 @@ export declare function envLoadHint(command?: string, version?: string): string;
|
|
|
364
374
|
* chain (env vars, `~/.aws/credentials`, SSO, instance profile…).
|
|
365
375
|
* The AWS SDK is imported lazily so env-only users don't need it at runtime.
|
|
366
376
|
*/
|
|
377
|
+
/**
|
|
378
|
+
* Does this look like a credential rather than the NAME of one?
|
|
379
|
+
*
|
|
380
|
+
* The shapes providers actually issue, plus a long opaque run. Deliberately narrow: an
|
|
381
|
+
* environment variable name is short, upper-case and underscore-separated, so a false
|
|
382
|
+
* positive here would refuse a legitimate name — and the cost of a false negative is only
|
|
383
|
+
* that the ordinary "not set" message is printed instead.
|
|
384
|
+
*/
|
|
385
|
+
export declare function looksLikeSecret(value: string): boolean;
|
|
386
|
+
/** The first and last few characters only — enough to recognise it, never enough to use it. */
|
|
387
|
+
export declare function maskSecret(value: string): string;
|
|
367
388
|
export declare function resolveSecret(ref: SecretRef): Promise<string>;
|
|
368
389
|
/**
|
|
369
390
|
* The app root the contracts describe, or undefined when config names none.
|