yam-flow 2.4.0 → 2.5.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/AGENTS.md +6 -4
- package/CHANGELOG.md +28 -0
- package/COMMANDS.md +13 -0
- package/README.md +48 -0
- package/ROADMAP.md +1 -0
- package/dist/bin/yam.js +87 -0
- package/dist/lib/external-updates.d.ts +103 -0
- package/dist/lib/external-updates.js +989 -0
- package/package.json +4 -3
- package/src/bin/yam.ts +88 -0
- package/src/lib/external-updates.ts +1150 -0
- package/yam.manifest.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -24,18 +24,20 @@
|
|
|
24
24
|
|
|
25
25
|
## Latest Update Trigger
|
|
26
26
|
|
|
27
|
-
When the user says exactly `yam 최신 업데이트 사항 반영해줘` in a chat for this project, treat that phrase as explicit authorization to
|
|
27
|
+
When the user says exactly `yam 최신 업데이트 사항 반영해줘` in a chat for this project, treat that phrase as explicit authorization to check and apply reviewed updates for yam, Scrapling, and Insane Search.
|
|
28
28
|
|
|
29
29
|
Run these commands sequentially from the yam project directory:
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
|
|
33
|
-
yam
|
|
32
|
+
yam update check --json
|
|
33
|
+
yam update apply --all --json
|
|
34
34
|
yam version
|
|
35
35
|
yam status
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
- Request any environment approval required for network access or writes outside the workspace.
|
|
39
39
|
- Stop at the first failed command, report the blocker, and do not claim that later steps ran.
|
|
40
|
-
-
|
|
40
|
+
- Treat that exact phrase as one-time authorization for this run only; do not create an unattended updater, hook, or automation.
|
|
41
|
+
- Never edit `.codex/plugins/cache` directly. If Insane Search cannot be updated in place through the official Codex CLI, report `manual_plugin_update_required` and stop.
|
|
42
|
+
- After all four commands pass, summarize every component receipt, report the installed yam version and skill status, then tell the user that Codex can be restarted to reload updated skills/plugins.
|
|
41
43
|
- Do not restart Codex automatically.
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to yam-flow are documented here.
|
|
4
4
|
|
|
5
|
+
## v2.5.0 - 2026-07-30
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added read-only `yam update check [--json]` for yam, Scrapling, and Insane Search with official registry/manifest versions and source-revision metadata.
|
|
10
|
+
- Added explicitly authorized component/all update application with per-component receipts, a concurrent-run lock, exact stable versions, verification evidence, and rollback guidance.
|
|
11
|
+
- Added isolated Scrapling version environments with `pip check`, HTTP extraction, browser extraction, and atomic executable switching; the previous environment remains available for rollback.
|
|
12
|
+
- Added deterministic temp-only updater smoke coverage, including receipt-failure symlink rollback, manual Codex plugin fallback, strict no-remove/cache behavior, and yam-last `--all` ordering.
|
|
13
|
+
|
|
14
|
+
### Improved
|
|
15
|
+
|
|
16
|
+
- The build now preserves executable mode on `dist/bin/yam.js`, and dist freshness fails if a global npm install would expose a non-executable `yam` target.
|
|
17
|
+
|
|
18
|
+
### Security
|
|
19
|
+
|
|
20
|
+
- Insane Search updates pin the official manifest to the exact observed Git revision and use only official Codex CLI commands. A missing safe in-place path returns `manual_plugin_update_required`; an unreadable post-upgrade state fails closed before `add`; yam never removes the plugin first or writes `.codex/plugins/cache` directly.
|
|
21
|
+
- External commands use argument arrays without a shell, version/component allowlists, redacted captured output, and fail-closed apply locking.
|
|
22
|
+
- Existing Scrapling links require a matching yam ownership marker, and an unwritable receipt stops `--all` before any later component mutation.
|
|
23
|
+
|
|
24
|
+
### Verification
|
|
25
|
+
|
|
26
|
+
- `npm run external-updates-smoke`
|
|
27
|
+
- `npm run typecheck`
|
|
28
|
+
- `npm run verify:self`
|
|
29
|
+
- `npm run cli-smoke`
|
|
30
|
+
- `npm run dist:freshness`
|
|
31
|
+
- `npm run package-boundary:check`
|
|
32
|
+
|
|
5
33
|
## v2.4.0 - 2026-07-27
|
|
6
34
|
|
|
7
35
|
### Added
|
package/COMMANDS.md
CHANGED
|
@@ -143,6 +143,19 @@ yam measure quick --files 3 --commands 1 --report-lines 5 --seconds 40
|
|
|
143
143
|
yam template tuning
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
+
## External Updates
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
yam update check
|
|
150
|
+
yam update check --json
|
|
151
|
+
yam update apply --component scrapling --json
|
|
152
|
+
yam update apply --component insane-search --json
|
|
153
|
+
yam update apply --component yam --json
|
|
154
|
+
yam update apply --all --json
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`yam update check` is read-only. Every `apply` needs explicit authorization, uses a concurrent-run lock, records a component receipt, and stops on failed/manual results. `--all` runs yam last. Insane Search is never remove-first and `.codex/plugins/cache` is never edited directly.
|
|
158
|
+
|
|
146
159
|
## Tool Doctor / Proof / Safety
|
|
147
160
|
|
|
148
161
|
Read-only readiness and evidence helpers. These do not install, deploy, query databases, or run verification by themselves.
|
package/README.md
CHANGED
|
@@ -130,6 +130,54 @@ Manual copying can still make a skill loadable, but it intentionally remains unv
|
|
|
130
130
|
|
|
131
131
|
Restart Codex after installing so skills reload.
|
|
132
132
|
|
|
133
|
+
## External Updates
|
|
134
|
+
|
|
135
|
+
Update checks are read-only and never authorize a mutation by themselves:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
yam update check
|
|
139
|
+
yam update check --json
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Apply requires an explicit component or `--all`:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
yam update apply --component scrapling
|
|
146
|
+
yam update apply --component insane-search
|
|
147
|
+
yam update apply --component yam
|
|
148
|
+
yam update apply --all --json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`--all` processes Scrapling, then Insane Search, then yam so the running updater replaces itself last. It stops at the first failed or manual-only component. Each attempt writes a component receipt under `~/.local/state/yam/update-receipts/` with the old/new version, source revision, checks, side effects, outcome, and rollback guidance. A lock prevents concurrent apply runs.
|
|
152
|
+
|
|
153
|
+
Scrapling updates install an exact stable PyPI version into a new versioned virtual environment. `pip check`, ordinary HTTP extraction, and headless browser extraction must pass before the executable symlink changes atomically; the previous environment is retained. An existing environment must have a matching `.yam-scrapling-install.json` ownership marker before yam will replace its link. Insane Search uses only the official `codex plugin` commands. yam never edits `.codex/plugins/cache`, never removes the plugin first, and returns `manual_plugin_update_required` when the installed Codex CLI cannot update it safely in place.
|
|
154
|
+
|
|
155
|
+
There is no unattended updater. The exact project trigger in `AGENTS.md` grants one-time authorization for a checked `--all` run, and Codex is never restarted automatically.
|
|
156
|
+
|
|
157
|
+
### Scrapling Usage
|
|
158
|
+
|
|
159
|
+
The isolated executable can be used directly from the shell:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
scrapling --version
|
|
163
|
+
scrapling extract get https://example.com example.txt --css-selector h1
|
|
164
|
+
scrapling extract stealthy-fetch https://example.com example-browser.txt --css-selector h1 --headless
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
From Python inside the installed environment:
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
from scrapling.fetchers import Fetcher, StealthyFetcher
|
|
171
|
+
|
|
172
|
+
page = Fetcher.get("https://example.com")
|
|
173
|
+
print(page.css("h1::text").get())
|
|
174
|
+
|
|
175
|
+
browser_page = StealthyFetcher.fetch("https://example.com", headless=True)
|
|
176
|
+
print(browser_page.css("h1::text").get())
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Use `Fetcher` for ordinary HTTP pages and `StealthyFetcher` only when a real browser or stronger anti-bot behavior is needed; the browser path is heavier.
|
|
180
|
+
|
|
133
181
|
## Uninstall
|
|
134
182
|
|
|
135
183
|
```bash
|
package/ROADMAP.md
CHANGED
|
@@ -97,6 +97,7 @@ Tasks:
|
|
|
97
97
|
- Add symlink-friendly local install.
|
|
98
98
|
- Add version command. Done.
|
|
99
99
|
- Add staged transactional install, rollback, receipt, and drift-aware status. Done.
|
|
100
|
+
- Add checked component updates for yam, Scrapling, and Insane Search with explicit authorization, receipts, and rollback guidance. Done.
|
|
100
101
|
- Consider npm packaging only after route behavior stabilizes.
|
|
101
102
|
|
|
102
103
|
### 8. Scout / Research Workflow
|
package/dist/bin/yam.js
CHANGED
|
@@ -11,6 +11,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
11
11
|
import { TRUTH_STATUSES, buildLoopReport, buildMediaGenerationProof, buildMissionCompletionGate, buildMissionPatchEnvelope, buildMissionSubagentReceipt, buildRollbackHint, buildRuntimeBackendEvidence, buildStudyNote, buildUeyeDesignCompletionGate, buildUeyeVisualProvenance, buildUeyeRunReport, buildUeyeSurfaceContext, buildYamCompletionProof, detectDbSafetyText as detectTrustDbSafetyText, isTruthStatus } from '../lib/trust-kernel.js';
|
|
12
12
|
import { archiveUeyeRevision, inspectImageFile, upsertUeyeAsset, verifyUeyeAssetManifest, verifyUeyeRevisionHistory } from '../lib/ueye-artifacts.js';
|
|
13
13
|
import { inspectSkillInstallation, installSkillSetTransactional, uninstallSkillSetSafely } from '../lib/skill-installation.js';
|
|
14
|
+
import { EXTERNAL_UPDATE_COMPONENTS, applyExternalUpdates, checkExternalUpdates } from '../lib/external-updates.js';
|
|
14
15
|
function errorMessage(error) {
|
|
15
16
|
return error instanceof Error ? error.message : String(error);
|
|
16
17
|
}
|
|
@@ -146,6 +147,9 @@ Usage:
|
|
|
146
147
|
yam mission queue [--agent-id id] [--scope text] [--changed file] [--verification-hint text] [--json]
|
|
147
148
|
yam mission receipt [--thread-id id] [--role reviewer] [--lifecycle stopped] [--outcome passed] [--evidence text] [--json]
|
|
148
149
|
yam mission gate [--expected-thread id] [--receipt file] [--json]
|
|
150
|
+
yam update check [--json]
|
|
151
|
+
yam update apply --component yam|scrapling|insane-search [--json]
|
|
152
|
+
yam update apply --all [--json]
|
|
149
153
|
yam benchmark report [--baseline n] [--current n] [--unit ms] [--target lower|higher] [--json]
|
|
150
154
|
yam release report [--json]
|
|
151
155
|
yam safety [text...]
|
|
@@ -3556,6 +3560,86 @@ async function mission(args = []) {
|
|
|
3556
3560
|
console.error(`unknown mission command: ${subcommand}`);
|
|
3557
3561
|
return missionUsage();
|
|
3558
3562
|
}
|
|
3563
|
+
async function updateCommand(args = []) {
|
|
3564
|
+
const subcommand = args[0] || 'help';
|
|
3565
|
+
if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h')
|
|
3566
|
+
return updateUsage();
|
|
3567
|
+
if (subcommand === 'check') {
|
|
3568
|
+
const rest = args.slice(1);
|
|
3569
|
+
const invalid = rest.filter((arg) => arg !== '--json');
|
|
3570
|
+
if (invalid.length || rest.filter((arg) => arg === '--json').length > 1) {
|
|
3571
|
+
throw new Error(`invalid update check option: ${invalid[0] || '--json repeated'}`);
|
|
3572
|
+
}
|
|
3573
|
+
const report = await checkExternalUpdates(VERSION);
|
|
3574
|
+
printJsonOrHuman(report, rest.includes('--json'), 'External update check');
|
|
3575
|
+
if (!report.success)
|
|
3576
|
+
process.exitCode = 1;
|
|
3577
|
+
return;
|
|
3578
|
+
}
|
|
3579
|
+
if (subcommand !== 'apply') {
|
|
3580
|
+
throw new Error(`unknown update command: ${subcommand}`);
|
|
3581
|
+
}
|
|
3582
|
+
const parsed = parseUpdateApplyArgs(args.slice(1));
|
|
3583
|
+
const report = await applyExternalUpdates(VERSION, {
|
|
3584
|
+
component: parsed.component,
|
|
3585
|
+
all: parsed.all
|
|
3586
|
+
});
|
|
3587
|
+
printJsonOrHuman(report, parsed.json, 'External update apply');
|
|
3588
|
+
if (!report.success)
|
|
3589
|
+
process.exitCode = 1;
|
|
3590
|
+
}
|
|
3591
|
+
function parseUpdateApplyArgs(args) {
|
|
3592
|
+
let component;
|
|
3593
|
+
let all = false;
|
|
3594
|
+
let json = false;
|
|
3595
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
3596
|
+
const arg = String(args[index] || '');
|
|
3597
|
+
if (arg === '--json') {
|
|
3598
|
+
if (json)
|
|
3599
|
+
throw new Error('--json may be supplied only once');
|
|
3600
|
+
json = true;
|
|
3601
|
+
continue;
|
|
3602
|
+
}
|
|
3603
|
+
if (arg === '--all') {
|
|
3604
|
+
if (all)
|
|
3605
|
+
throw new Error('--all may be supplied only once');
|
|
3606
|
+
all = true;
|
|
3607
|
+
continue;
|
|
3608
|
+
}
|
|
3609
|
+
if (arg === '--component' || arg.startsWith('--component=')) {
|
|
3610
|
+
if (component)
|
|
3611
|
+
throw new Error('--component may be supplied only once');
|
|
3612
|
+
const value = arg === '--component' ? String(args[index + 1] || '') : arg.slice('--component='.length);
|
|
3613
|
+
if (arg === '--component')
|
|
3614
|
+
index += 1;
|
|
3615
|
+
if (!EXTERNAL_UPDATE_COMPONENTS.includes(value)) {
|
|
3616
|
+
throw new Error('component must be one of: yam, scrapling, insane-search');
|
|
3617
|
+
}
|
|
3618
|
+
component = value;
|
|
3619
|
+
continue;
|
|
3620
|
+
}
|
|
3621
|
+
throw new Error(`invalid update apply option: ${arg}`);
|
|
3622
|
+
}
|
|
3623
|
+
if ((all && component) || (!all && !component)) {
|
|
3624
|
+
throw new Error('choose exactly one of --component yam|scrapling|insane-search or --all');
|
|
3625
|
+
}
|
|
3626
|
+
return { component, all, json };
|
|
3627
|
+
}
|
|
3628
|
+
function updateUsage() {
|
|
3629
|
+
console.log(`yam update
|
|
3630
|
+
|
|
3631
|
+
Usage:
|
|
3632
|
+
yam update check [--json]
|
|
3633
|
+
yam update apply --component yam|scrapling|insane-search [--json]
|
|
3634
|
+
yam update apply --all [--json]
|
|
3635
|
+
|
|
3636
|
+
Safety:
|
|
3637
|
+
check is read-only. apply requires an explicit component or --all.
|
|
3638
|
+
--all updates Scrapling and Insane Search before yam, then stops at the first failed/manual component.
|
|
3639
|
+
Insane Search is updated only through the official Codex CLI; plugin caches are never edited directly.
|
|
3640
|
+
Every apply attempt records component checks, outcome, side effects, and rollback guidance.
|
|
3641
|
+
`);
|
|
3642
|
+
}
|
|
3559
3643
|
function missionUsage() {
|
|
3560
3644
|
console.log(`yam mission
|
|
3561
3645
|
|
|
@@ -5313,6 +5397,7 @@ function showRequestedHelp(args) {
|
|
|
5313
5397
|
media: mediaUsage,
|
|
5314
5398
|
runtime: runtimeUsage,
|
|
5315
5399
|
mission: missionUsage,
|
|
5400
|
+
update: updateUsage,
|
|
5316
5401
|
benchmark: benchmarkUsage,
|
|
5317
5402
|
memory: memoryUsage
|
|
5318
5403
|
};
|
|
@@ -5360,6 +5445,8 @@ async function main() {
|
|
|
5360
5445
|
return runtime(process.argv.slice(3));
|
|
5361
5446
|
if (command === 'mission')
|
|
5362
5447
|
return mission(process.argv.slice(3));
|
|
5448
|
+
if (command === 'update')
|
|
5449
|
+
return updateCommand(process.argv.slice(3));
|
|
5363
5450
|
if (command === 'benchmark')
|
|
5364
5451
|
return benchmark(process.argv.slice(3));
|
|
5365
5452
|
if (command === 'release')
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export declare const EXTERNAL_UPDATE_COMPONENTS: readonly ["yam", "scrapling", "insane-search"];
|
|
2
|
+
export type ExternalUpdateComponent = typeof EXTERNAL_UPDATE_COMPONENTS[number];
|
|
3
|
+
type JsonRecord = Record<string, any>;
|
|
4
|
+
export interface ExternalCommandResult {
|
|
5
|
+
ok: boolean;
|
|
6
|
+
status: number | null;
|
|
7
|
+
stdout: string;
|
|
8
|
+
stderr: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ExternalCommandOptions {
|
|
11
|
+
cwd?: string;
|
|
12
|
+
env?: NodeJS.ProcessEnv;
|
|
13
|
+
timeout?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ExternalUpdatePaths {
|
|
16
|
+
stateDir: string;
|
|
17
|
+
receiptDir: string;
|
|
18
|
+
lockFile: string;
|
|
19
|
+
scraplingRoot: string;
|
|
20
|
+
scraplingBin: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ExternalUpdateDependencies {
|
|
23
|
+
run?: (command: string, args: string[], options?: ExternalCommandOptions) => Promise<ExternalCommandResult> | ExternalCommandResult;
|
|
24
|
+
fetchJson?: (url: string) => Promise<any>;
|
|
25
|
+
now?: () => Date;
|
|
26
|
+
homeDir?: string;
|
|
27
|
+
env?: NodeJS.ProcessEnv;
|
|
28
|
+
paths?: Partial<ExternalUpdatePaths>;
|
|
29
|
+
removeCandidate?: (candidate: string, root: string) => Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
export interface ExternalComponentCheck {
|
|
32
|
+
component: ExternalUpdateComponent;
|
|
33
|
+
installed_version: string;
|
|
34
|
+
latest_version: string;
|
|
35
|
+
update_available: boolean;
|
|
36
|
+
status: 'up_to_date' | 'update_available' | 'not_installed' | 'check_failed';
|
|
37
|
+
source: string;
|
|
38
|
+
source_revision?: {
|
|
39
|
+
local: string;
|
|
40
|
+
remote: string;
|
|
41
|
+
drift: boolean;
|
|
42
|
+
note: string;
|
|
43
|
+
};
|
|
44
|
+
executable?: string;
|
|
45
|
+
error?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface ExternalComponentReceipt {
|
|
48
|
+
schema: 'yam.external-update-receipt.v1';
|
|
49
|
+
generated_at: string;
|
|
50
|
+
component: ExternalUpdateComponent;
|
|
51
|
+
outcome: 'up_to_date' | 'updated' | 'failed' | 'manual_plugin_update_required';
|
|
52
|
+
previous_version: string;
|
|
53
|
+
requested_version: string;
|
|
54
|
+
installed_version: string;
|
|
55
|
+
source: string;
|
|
56
|
+
source_revision: JsonRecord | null;
|
|
57
|
+
checks: Array<{
|
|
58
|
+
id: string;
|
|
59
|
+
status: 'passed' | 'failed' | 'skipped';
|
|
60
|
+
note: string;
|
|
61
|
+
}>;
|
|
62
|
+
rollback_hint: {
|
|
63
|
+
automatic: boolean;
|
|
64
|
+
previous_target: string;
|
|
65
|
+
guidance: string;
|
|
66
|
+
};
|
|
67
|
+
side_effects: string[];
|
|
68
|
+
error: string;
|
|
69
|
+
truth_status: 'verified' | 'partial' | 'blocked';
|
|
70
|
+
persistence?: 'written' | 'failed';
|
|
71
|
+
receipt_path?: string;
|
|
72
|
+
receipt_error?: string;
|
|
73
|
+
}
|
|
74
|
+
export declare function checkExternalUpdates(currentYamVersion: string, dependencies?: ExternalUpdateDependencies): Promise<{
|
|
75
|
+
schema: string;
|
|
76
|
+
generated_at: string;
|
|
77
|
+
mutation_authorized: boolean;
|
|
78
|
+
components: [ExternalComponentCheck, ExternalComponentCheck, ExternalComponentCheck];
|
|
79
|
+
update_available: boolean;
|
|
80
|
+
success: boolean;
|
|
81
|
+
truth_status: string;
|
|
82
|
+
next_action: string;
|
|
83
|
+
}>;
|
|
84
|
+
export declare function applyExternalUpdates(currentYamVersion: string, selection: {
|
|
85
|
+
component?: ExternalUpdateComponent;
|
|
86
|
+
all?: boolean;
|
|
87
|
+
}, dependencies?: ExternalUpdateDependencies): Promise<{
|
|
88
|
+
schema: string;
|
|
89
|
+
generated_at: string;
|
|
90
|
+
mutation_authorized: boolean;
|
|
91
|
+
requested_components: ("yam" | "scrapling" | "insane-search")[];
|
|
92
|
+
applied_components: ("yam" | "scrapling" | "insane-search")[];
|
|
93
|
+
receipts: ExternalComponentReceipt[];
|
|
94
|
+
stopped_early: boolean;
|
|
95
|
+
lock: {
|
|
96
|
+
path: string;
|
|
97
|
+
released: boolean;
|
|
98
|
+
};
|
|
99
|
+
success: boolean;
|
|
100
|
+
truth_status: string;
|
|
101
|
+
next_action: string;
|
|
102
|
+
}>;
|
|
103
|
+
export {};
|