@ai-dossier/cli 0.9.1 → 0.10.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 +277 -0
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/keys.d.ts.map +1 -1
- package/dist/commands/keys.js +113 -28
- package/dist/commands/keys.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +4 -3
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/runstate.d.ts +12 -0
- package/dist/commands/runstate.d.ts.map +1 -0
- package/dist/commands/runstate.js +572 -0
- package/dist/commands/runstate.js.map +1 -0
- package/dist/helpers.d.ts +8 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +17 -0
- package/dist/helpers.js.map +1 -1
- package/dist/runstate.d.ts +177 -0
- package/dist/runstate.d.ts.map +1 -0
- package/dist/runstate.js +438 -0
- package/dist/runstate.js.map +1 -0
- package/dist/verify-dossier.d.ts.map +1 -1
- package/dist/verify-dossier.js +43 -22
- package/dist/verify-dossier.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -311,6 +311,254 @@ ai-dossier skill-export my-skill --version 2.0.0 --changelog "Add range support"
|
|
|
311
311
|
|
|
312
312
|
---
|
|
313
313
|
|
|
314
|
+
## Runstate — workflow milestones
|
|
315
|
+
|
|
316
|
+
Issue-workflow dossiers (`imboard-ai/git/full-cycle-issue` and friends) record their
|
|
317
|
+
progress by appending a `<!-- runstate:v1 -->` comment to the GitHub issue after every
|
|
318
|
+
phase. That trail is the only run state that survives a session: it is what lets a later
|
|
319
|
+
run resume mid-workflow instead of starting over.
|
|
320
|
+
|
|
321
|
+
Until now the milestone was a markdown heredoc that each agent reproduced by hand, and
|
|
322
|
+
agents silently got it wrong — skipping milestones entirely, or pasting `$(date …)` into
|
|
323
|
+
the comment verbatim. `ai-dossier runstate` makes it a command: the timestamp is filled
|
|
324
|
+
in for you, and phase/status/required keys are validated *before* anything is posted.
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
# Mint the run id once, at the gate phase
|
|
328
|
+
ai-dossier runstate mint --issue 440 # -> r-440-ab56
|
|
329
|
+
|
|
330
|
+
# Post a milestone at the end of each phase
|
|
331
|
+
ai-dossier runstate post --issue 440 \
|
|
332
|
+
--phase setup --status done --run r-440-ab56 \
|
|
333
|
+
--kv branch=feature/440-runstate \
|
|
334
|
+
--kv worktree=/repo/worktrees/feature-440-runstate \
|
|
335
|
+
--kv pool_claimed=false \
|
|
336
|
+
--kv base_branch=main
|
|
337
|
+
|
|
338
|
+
# Read the last milestone back
|
|
339
|
+
ai-dossier runstate last --issue 440 --json
|
|
340
|
+
|
|
341
|
+
# Ask where a run should resume from
|
|
342
|
+
ai-dossier runstate verify --issue 440
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Subcommands
|
|
346
|
+
|
|
347
|
+
| Command | What it does | Writes? |
|
|
348
|
+
|---|---|---|
|
|
349
|
+
| `post` | Validates and posts one milestone comment via `gh issue comment` | yes |
|
|
350
|
+
| `last` | Prints the most recent milestone on the issue, parsed | no |
|
|
351
|
+
| `verify` | Runs the gate's resume verification and prints `resume_from` + `resume_context` | no |
|
|
352
|
+
| `mint` | Prints a fresh run id (`r-<issue>-<hex>`) | no |
|
|
353
|
+
|
|
354
|
+
`last` and `verify` are strictly read-only — they only run `gh issue view`, `gh pr view`,
|
|
355
|
+
`git ls-remote`/`rev-parse`, and `stat`, so they work fine without push access to the
|
|
356
|
+
repository.
|
|
357
|
+
|
|
358
|
+
Every subcommand takes `--issue <n>` (a positive integer — the number only, not a URL or
|
|
359
|
+
a `#`-prefixed string). `post`, `last`, and `verify` additionally take `--repo
|
|
360
|
+
<owner/name>` (a bare slug, not a URL; defaults to the repository `gh` resolves for the
|
|
361
|
+
current directory) and `--json`. `mint` takes `--issue` and nothing else.
|
|
362
|
+
|
|
363
|
+
`last` prints the milestone's own `key=value` lines, so the output is already in the
|
|
364
|
+
shape a shell or a dossier reads:
|
|
365
|
+
|
|
366
|
+
```
|
|
367
|
+
$ ai-dossier runstate last --issue 440
|
|
368
|
+
phase=setup
|
|
369
|
+
status=done
|
|
370
|
+
run=r-440-ab56
|
|
371
|
+
at=2026-08-24T07:59:32Z
|
|
372
|
+
branch=feature/440-runstate
|
|
373
|
+
worktree=/repo/worktrees/feature-440-runstate
|
|
374
|
+
pool_claimed=false
|
|
375
|
+
base_branch=main
|
|
376
|
+
next=plan
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
With `--json` the same keys come back as one flat object; an issue with no milestones
|
|
380
|
+
prints `No runstate milestones on issue #440.` (or `null` under `--json`) and exits 0.
|
|
381
|
+
|
|
382
|
+
### `post`
|
|
383
|
+
|
|
384
|
+
| Flag | Meaning |
|
|
385
|
+
|---|---|
|
|
386
|
+
| `--issue <n>` | Issue to comment on (required) |
|
|
387
|
+
| `--phase <p>` | `gate`, `setup`, `plan`, `implement`, `review`, `ship`, `report` (required) |
|
|
388
|
+
| `--status <s>` | `done`, `partial`, `blocked`, `awaiting-merge` (required) |
|
|
389
|
+
| `--run <id>` | Run id minted at the gate phase (required) |
|
|
390
|
+
| `--kv <key=value...>` | Phase-specific key, repeatable (and variadic: `--kv a=1 b=2` works too) |
|
|
391
|
+
| `--next <phase>` | Override the computed `next=` line — a phase name or `done` |
|
|
392
|
+
| `--repo <owner/name>` | Target repository (defaults to the current one) |
|
|
393
|
+
| `--dry-run` | Print the comment body instead of posting it |
|
|
394
|
+
| `--json` | Machine-readable output |
|
|
395
|
+
|
|
396
|
+
Validation failures print one actionable message per problem on stderr and exit 1
|
|
397
|
+
**without posting**:
|
|
398
|
+
|
|
399
|
+
```
|
|
400
|
+
$ ai-dossier runstate post --issue 440 --phase setup --status done --run r-440-ab56 --kv branch=x
|
|
401
|
+
❌ Phase 'setup' with status 'done' requires worktree= pool_claimed= base_branch= — add with --kv worktree=<value> --kv pool_claimed=<value> --kv base_branch=<value>
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
A valid `--dry-run` prints the exact body that would be posted:
|
|
405
|
+
|
|
406
|
+
```
|
|
407
|
+
$ ai-dossier runstate post --issue 440 --phase setup --status done --run r-440-ab56 \
|
|
408
|
+
--kv branch=feature/440-runstate --kv worktree=/repo/worktrees/feature-440-runstate \
|
|
409
|
+
--kv pool_claimed=false --kv base_branch=main --dry-run
|
|
410
|
+
<!-- runstate:v1 -->
|
|
411
|
+
phase=setup status=done run=r-440-ab56 at=2026-08-24T07:59:32Z
|
|
412
|
+
branch=feature/440-runstate
|
|
413
|
+
worktree=/repo/worktrees/feature-440-runstate
|
|
414
|
+
pool_claimed=false
|
|
415
|
+
base_branch=main
|
|
416
|
+
next=plan
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Without `--dry-run` a successful post prints `✅ setup done → <comment url>`; `--json`
|
|
420
|
+
returns `{ "posted": true, "url": …, "body": … }` (and `{ "posted": false, "dryRun":
|
|
421
|
+
true, "body": … }` under `--dry-run`). See [When `gh` or `git`
|
|
422
|
+
fails](#when-gh-or-git-fails) for what a failed post prints.
|
|
423
|
+
|
|
424
|
+
### Phases, statuses, and required keys
|
|
425
|
+
|
|
426
|
+
This table is the executable copy of the "Runstate Milestones" table in
|
|
427
|
+
`imboard-ai/git/full-cycle-issue@3.8.0`:
|
|
428
|
+
|
|
429
|
+
| Phase | Statuses | Required keys |
|
|
430
|
+
|---|---|---|
|
|
431
|
+
| `gate` | `done`, `blocked` | `base_branch` `warnings` |
|
|
432
|
+
| `setup` | `done`, `blocked` | `branch` `worktree` `pool_claimed` `base_branch` |
|
|
433
|
+
| `plan` | `done`, `blocked` | `planning` `head` `open_questions` `visual_review` |
|
|
434
|
+
| `implement` | `done`, `blocked` | `head` `files` `tests_added` `tests_run` `ci_parity` |
|
|
435
|
+
| `review` | `done`, `partial`, `blocked` | `head` `fixed` `escalated` `agents_done` `agents_pending` |
|
|
436
|
+
| `ship` (1st, before the CI wait) | `awaiting-merge` | `pr` `head` `ci_fix_attempts` |
|
|
437
|
+
| `ship` (2nd, after merge + teardown) | `done`, `blocked` | `pr` `merge_commit` `ci_fix_attempts` `cleanup` |
|
|
438
|
+
| `report` | `done` | `pr` `traps_added` |
|
|
439
|
+
|
|
440
|
+
The `Statuses` column is a closed set: a status not listed for a phase is rejected, so
|
|
441
|
+
`report` cannot be `blocked` and only `ship` may be `awaiting-merge`. Any phase that
|
|
442
|
+
*can* report `status=blocked` must also carry `reason=<short-slug>` when it does.
|
|
443
|
+
|
|
444
|
+
`next=` is computed for you: the linear order `gate → setup → plan → implement → review →
|
|
445
|
+
ship → report → done`, except that `blocked` ends the run (`next=done`) and the two
|
|
446
|
+
non-terminal statuses stay in their own phase — `ship`/`awaiting-merge` is followed by a
|
|
447
|
+
second `ship` milestone, and a `partial` review still has agents to run. Use `--next` to
|
|
448
|
+
override.
|
|
449
|
+
|
|
450
|
+
### Key and value rules
|
|
451
|
+
|
|
452
|
+
Every `--kv` pair is checked before anything is posted:
|
|
453
|
+
|
|
454
|
+
- Keys are `lower_snake_case` (`^[a-z][a-z0-9_]*$`) and may appear at most once.
|
|
455
|
+
- No empty values — omit the key instead.
|
|
456
|
+
- No `$` in values. This is the check that catches an unexpanded `$(date …)` before it
|
|
457
|
+
reaches the issue.
|
|
458
|
+
- One line per value. A newline would split into extra `key=` lines that readers parse as
|
|
459
|
+
real state, so it is rejected for **every** key, including `ac*` ones — collapse it
|
|
460
|
+
(`,` or ` / `) instead.
|
|
461
|
+
- No spaces in values (use `-` or `,`). Only `ac*` keys — `ac`, `ac1`, `ac_results` — are
|
|
462
|
+
exempt, because acceptance-criterion lines are prose. (The newline rule above still
|
|
463
|
+
applies to them.)
|
|
464
|
+
- A value is at most 4000 characters and the whole comment at most 60000 (GitHub rejects
|
|
465
|
+
a longer issue comment with an opaque 422). A milestone is an index, not a report:
|
|
466
|
+
replace a long value with a count or a path to the full text.
|
|
467
|
+
- `worktree=` and `planning=` must be absolute paths, so a resume from a different
|
|
468
|
+
working directory can still find them.
|
|
469
|
+
- `--next` must be a phase name or `done`. It is written to the comment verbatim, so an
|
|
470
|
+
unchecked typo would point the next resume at a phase that does not exist.
|
|
471
|
+
- Comments are append-only: never edit or delete a prior milestone.
|
|
472
|
+
|
|
473
|
+
### When `gh` or `git` fails
|
|
474
|
+
|
|
475
|
+
Every subcommand exits **1** on failure with the cause *and its fix* on stderr, so a
|
|
476
|
+
calling dossier can branch on the exit code and an agent knows what to do next. The three
|
|
477
|
+
causes that look identical from the outside are reported as three different things:
|
|
478
|
+
|
|
479
|
+
```
|
|
480
|
+
$ ai-dossier runstate last --issue 440
|
|
481
|
+
❌ Could not read issue #440: gh is not authenticated.
|
|
482
|
+
Fix: run 'gh auth login', confirm with 'gh auth status', then re-run.
|
|
483
|
+
gh said: To get started with GitHub CLI, please run: gh auth login
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
| Cause | What you get |
|
|
487
|
+
|---|---|
|
|
488
|
+
| `gh` not on PATH | "'gh' is not installed, or is not on PATH" + the install link |
|
|
489
|
+
| Not logged in | "gh is not authenticated" + `gh auth login` |
|
|
490
|
+
| Issue/PR does not exist | "GitHub could not find it in …" + a nudge to check `--repo` |
|
|
491
|
+
| No permission (403) | "the authenticated account lacks access to …" + `gh auth status` |
|
|
492
|
+
| github.com unreachable | "gh could not reach GitHub" + retry guidance |
|
|
493
|
+
| Anything else | the exit status, and "run the same gh command by hand" |
|
|
494
|
+
|
|
495
|
+
`gh`'s own stderr is **always** echoed on a `gh said:` line, including for causes the CLI
|
|
496
|
+
cannot classify — nothing is swallowed. `gh` exiting 0 with output that is not JSON, or
|
|
497
|
+
with JSON that has no `comments` array, is also a hard failure rather than a silent "no
|
|
498
|
+
milestones": the two must never look alike, because reading a fresh run out of a broken
|
|
499
|
+
response makes a resume start over and throw away finished work.
|
|
500
|
+
|
|
501
|
+
When `post` cannot reach GitHub, it prints the exact `gh issue comment …` command to run
|
|
502
|
+
by hand. The milestone is the only durable record of the phase, so re-running the phase
|
|
503
|
+
costs far more than retrying the comment.
|
|
504
|
+
|
|
505
|
+
### `verify`
|
|
506
|
+
|
|
507
|
+
`verify` implements `imboard-ai/git/gate-issue`'s resume table. It never trusts the
|
|
508
|
+
comment alone — each claim is checked against reality (is the branch still on the remote,
|
|
509
|
+
does the worktree still exist, does the planning file exist, has HEAD moved, what is the
|
|
510
|
+
PR's state) before it reports where to resume:
|
|
511
|
+
|
|
512
|
+
```
|
|
513
|
+
$ ai-dossier runstate verify --issue 440
|
|
514
|
+
resume_from=implement
|
|
515
|
+
run_id=r-440-ab56
|
|
516
|
+
verified=branch,worktree,planning
|
|
517
|
+
resume_context={"branch":"feature/440-runstate","worktree":"/repo/worktrees/feature-440-runstate",...}
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
`resume_from` is a phase name, or one of:
|
|
521
|
+
|
|
522
|
+
| Value | Meaning |
|
|
523
|
+
|---|---|
|
|
524
|
+
| `none` | No milestones on the issue — a fresh run |
|
|
525
|
+
| `ship-wait` | The PR is open and mergeable; re-enter `ship` at the CI wait |
|
|
526
|
+
| `ship-teardown` | The PR is already merged; re-enter `ship` at post-merge cleanup |
|
|
527
|
+
| `done` | The `report` milestone is posted and the issue is closed (`note=already complete`) |
|
|
528
|
+
|
|
529
|
+
A failed check sends the resume *backwards*, never forwards: if the branch is gone from
|
|
530
|
+
the remote or the worktree no longer exists, a `plan`/`implement`/`review` milestone
|
|
531
|
+
still yields `resume_from=setup`. A milestone with `status=blocked` resumes at its own
|
|
532
|
+
phase.
|
|
533
|
+
|
|
534
|
+
`resume_context` is merged across the run's milestones (later ones winning), so a resume
|
|
535
|
+
at `plan` still sees `branch`/`worktree` from the `setup` milestone. (The dossier's own
|
|
536
|
+
table carries only the last milestone's keys; merging is what makes a mid-run resume
|
|
537
|
+
self-sufficient.) If the last three milestones are all `blocked` on the same phase,
|
|
538
|
+
`verify` adds `hard_block=resume-loop` — the run is looping and needs a human.
|
|
539
|
+
|
|
540
|
+
When a check cannot run at all — `git`/`gh` is missing or the remote is unreachable, or
|
|
541
|
+
the milestone's `branch=`/`worktree=`/`pr=` value is not one `verify` will hand to a
|
|
542
|
+
subprocess — that check degrades to "not verified" and `verify` warns instead of failing.
|
|
543
|
+
It still exits 0, because a conservative `resume_from` is the safe direction (redo a phase
|
|
544
|
+
rather than skip one), but the reader needs to know the answer is conservative *because a
|
|
545
|
+
check could not run*. Warnings go to stderr so stdout stays parseable:
|
|
546
|
+
|
|
547
|
+
```
|
|
548
|
+
⚠️ verify could not check everything: could not reach 'origin' to confirm branch 'feature/440-runstate' (git exited 128: fatal: 'origin' does not appear to be a git repository) — treating it as missing
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
Anyone who can comment on the issue can post a `<!-- runstate:v1 -->` body, so the values
|
|
552
|
+
`verify` reads back are untrusted input. Nothing is run through a shell, and a value that
|
|
553
|
+
would be read as a flag (leading `-`) or that is not the absolute path the protocol
|
|
554
|
+
requires is refused rather than passed to `git`/`gh` — it becomes one of the warnings
|
|
555
|
+
above.
|
|
556
|
+
|
|
557
|
+
`--json` returns the same fields as an object (`resume_from`, `run_id`, `verified`,
|
|
558
|
+
`resume_context`, plus `hard_block`, `note`, and `warnings` when they apply).
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
314
562
|
## Cache and Version Resolution
|
|
315
563
|
|
|
316
564
|
The CLI maintains a local cache at `~/.dossier/cache/`:
|
|
@@ -523,6 +771,35 @@ Project registries are merged with user registries. User-configured registries t
|
|
|
523
771
|
- ❌ Invalid → Signature failed → **BLOCK**
|
|
524
772
|
- ⚠️ No signature → Unsigned (warn for high-risk)
|
|
525
773
|
|
|
774
|
+
#### Managing trusted keys
|
|
775
|
+
|
|
776
|
+
Trust is a local decision: a valid signature from a key you have not added is
|
|
777
|
+
reported as untrusted, never auto-trusted. Keys live in `~/.dossier/trusted-keys.txt`,
|
|
778
|
+
one `<public-key> <identifier>` per line.
|
|
779
|
+
|
|
780
|
+
```bash
|
|
781
|
+
ai-dossier keys generate --name my-key # new Ed25519 pair in ~/.dossier/
|
|
782
|
+
ai-dossier keys list # what is trusted right now
|
|
783
|
+
ai-dossier keys add <public-key> <identifier>
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
`keys add` accepts a raw 44-char base64 key, an SPKI PEM block, or a legacy
|
|
787
|
+
minisign `RWT...` key, and stores the **canonical raw base64** form regardless —
|
|
788
|
+
so a key added in one encoding still matches a signature carrying another.
|
|
789
|
+
Anything it cannot interpret (a typo, a truncated key, a path to a `.pub` file)
|
|
790
|
+
is rejected outright rather than written and silently never matching.
|
|
791
|
+
|
|
792
|
+
> **Passing a PEM needs `--`.** A PEM begins with `-`, which the option parser
|
|
793
|
+
> reads as a flag:
|
|
794
|
+
> ```bash
|
|
795
|
+
> ai-dossier keys add -- "$(cat ~/.dossier/my-key.pub)" "my-key"
|
|
796
|
+
> ```
|
|
797
|
+
> `ai-dossier verify <dossier>` sidesteps this entirely — it prints a
|
|
798
|
+
> ready-to-run `keys add` command with the key already in base64 form.
|
|
799
|
+
|
|
800
|
+
If `keys list` warns about unusable entries, those keys are **not** trusted; the
|
|
801
|
+
warning names the line to fix.
|
|
802
|
+
|
|
526
803
|
### 3. Risk Assessment
|
|
527
804
|
|
|
528
805
|
**Analyzes**:
|
package/dist/cli.js
CHANGED
|
@@ -32,6 +32,7 @@ const pull_1 = require("./commands/pull");
|
|
|
32
32
|
const remove_1 = require("./commands/remove");
|
|
33
33
|
const reset_hooks_1 = require("./commands/reset-hooks");
|
|
34
34
|
const run_1 = require("./commands/run");
|
|
35
|
+
const runstate_1 = require("./commands/runstate");
|
|
35
36
|
const search_1 = require("./commands/search");
|
|
36
37
|
const sign_1 = require("./commands/sign");
|
|
37
38
|
const skill_export_1 = require("./commands/skill-export");
|
|
@@ -95,6 +96,7 @@ Agent-friendly: Run \`ai-dossier --agent\` for machine-readable capabilities.`);
|
|
|
95
96
|
(0, history_1.registerHistoryCommand)(commander_1.program);
|
|
96
97
|
(0, doctor_1.registerDoctorCommand)(commander_1.program);
|
|
97
98
|
(0, traces_1.registerTracesCommand)(commander_1.program);
|
|
99
|
+
(0, runstate_1.registerRunstateCommand)(commander_1.program);
|
|
98
100
|
// Hidden
|
|
99
101
|
(0, prompt_hook_1.registerPromptHookCommand)(commander_1.program);
|
|
100
102
|
(0, reset_hooks_1.registerResetHooksCommand)(commander_1.program);
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/commands/keys.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/commands/keys.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuBzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA2Q1D"}
|
package/dist/commands/keys.js
CHANGED
|
@@ -8,6 +8,24 @@ const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
|
8
8
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
9
|
const node_os_1 = __importDefault(require("node:os"));
|
|
10
10
|
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
+
const core_1 = require("@ai-dossier/core");
|
|
12
|
+
/**
|
|
13
|
+
* A one-line rendering of a rejected key argument, for the error message.
|
|
14
|
+
*
|
|
15
|
+
* Multi-line and overlong values are collapsed so the error stays readable, but
|
|
16
|
+
* the head of the value is kept — that is what lets someone see they pasted a
|
|
17
|
+
* path, a private key, or a truncated string.
|
|
18
|
+
*/
|
|
19
|
+
function summarizeKeyArgument(value) {
|
|
20
|
+
const flat = value.trim().replace(/\s+/g, ' ');
|
|
21
|
+
const shown = flat.length > 70 ? `${flat.slice(0, 70)}...` : flat;
|
|
22
|
+
return `"${shown}" (${value.trim().length} chars)`;
|
|
23
|
+
}
|
|
24
|
+
/** Whether an argument looks like someone passed a file path instead of a key. */
|
|
25
|
+
function looksLikeFilePath(value) {
|
|
26
|
+
const trimmed = value.trim();
|
|
27
|
+
return (/^[~.]?\//.test(trimmed) || /\.(pub|pem|key|txt)$/i.test(trimmed) || node_fs_1.default.existsSync(trimmed));
|
|
28
|
+
}
|
|
11
29
|
function registerKeysCommand(program) {
|
|
12
30
|
const keysCmd = program.command('keys').description('Manage trusted signing keys');
|
|
13
31
|
keysCmd
|
|
@@ -34,20 +52,18 @@ function registerKeysCommand(program) {
|
|
|
34
52
|
const publicPem = publicKey.export({ type: 'spki', format: 'pem' });
|
|
35
53
|
node_fs_1.default.writeFileSync(privatePath, privatePem, { mode: 0o600 });
|
|
36
54
|
node_fs_1.default.writeFileSync(publicPath, publicPem, { mode: 0o644 });
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const rawPublicKey = publicDer.subarray(publicDer.length - 32);
|
|
41
|
-
const publicKeyBase64 = rawPublicKey.toString('base64');
|
|
55
|
+
// The canonical shareable form is the raw 32-byte key in base64 — the same
|
|
56
|
+
// form the signer emits and the trust list stores.
|
|
57
|
+
const publicKeyBase64 = (0, core_1.normalizePublicKey)(publicPem);
|
|
42
58
|
console.log(`✅ Key pair generated successfully`);
|
|
43
59
|
console.log(` Name: ${options.name}`);
|
|
44
60
|
console.log(` Private key: ${privatePath}`);
|
|
45
61
|
console.log(` Public key: ${publicPath}`);
|
|
46
62
|
console.log(` Public key (base64): ${publicKeyBase64}`);
|
|
47
63
|
console.log('\nTo sign a dossier:');
|
|
48
|
-
console.log(` dossier sign <file> --method ed25519 --key ${privatePath}`);
|
|
64
|
+
console.log(` ai-dossier sign <file> --method ed25519 --key ${privatePath}`);
|
|
49
65
|
console.log('\nTo add this public key as trusted:');
|
|
50
|
-
console.log(` dossier keys add ${publicKeyBase64} "${options.name}"\n`);
|
|
66
|
+
console.log(` ai-dossier keys add ${publicKeyBase64} "${options.name}"\n`);
|
|
51
67
|
process.exit(0);
|
|
52
68
|
});
|
|
53
69
|
keysCmd
|
|
@@ -72,24 +88,34 @@ function registerKeysCommand(program) {
|
|
|
72
88
|
});
|
|
73
89
|
}
|
|
74
90
|
}
|
|
75
|
-
//
|
|
76
|
-
|
|
91
|
+
// Parse through core so this listing reflects exactly what verification
|
|
92
|
+
// trusts — including legacy multi-line PEM entries, which a line-oriented
|
|
93
|
+
// read here would report as several bogus keys.
|
|
94
|
+
let trustedEntries = [];
|
|
95
|
+
// Lines the parser had to skip. `keys list` is where someone lands when a
|
|
96
|
+
// key "isn't trusted", so this is the one place that must show them.
|
|
97
|
+
let trustedProblems = [];
|
|
77
98
|
if (node_fs_1.default.existsSync(trustedKeysPath)) {
|
|
78
|
-
const
|
|
79
|
-
|
|
99
|
+
const parsed = (0, core_1.parseTrustedKeys)(node_fs_1.default.readFileSync(trustedKeysPath, 'utf8'));
|
|
100
|
+
trustedEntries = parsed.entries;
|
|
101
|
+
trustedProblems = parsed.problems;
|
|
80
102
|
}
|
|
81
103
|
if (options.json) {
|
|
82
|
-
const trusted =
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
104
|
+
const trusted = trustedEntries.map((entry) => ({
|
|
105
|
+
type: 'trusted',
|
|
106
|
+
// As written in the file, plus the canonical form actually compared
|
|
107
|
+
// against a signature — a legacy entry differs between the two.
|
|
108
|
+
public_key: entry.publicKey,
|
|
109
|
+
normalized_public_key: (0, core_1.normalizePublicKey)(entry.publicKey),
|
|
110
|
+
identifier: entry.keyId,
|
|
111
|
+
}));
|
|
86
112
|
const generated = generatedKeys.map((k) => ({
|
|
87
113
|
type: 'generated',
|
|
88
114
|
name: k.name,
|
|
89
115
|
private_key: k.privatePath,
|
|
90
116
|
public_key: k.publicPath,
|
|
91
117
|
}));
|
|
92
|
-
console.log(JSON.stringify({ trusted, generated }, null, 2));
|
|
118
|
+
console.log(JSON.stringify({ trusted, generated, problems: trustedProblems }, null, 2));
|
|
93
119
|
process.exit(0);
|
|
94
120
|
}
|
|
95
121
|
// Display generated keys
|
|
@@ -107,7 +133,10 @@ function registerKeysCommand(program) {
|
|
|
107
133
|
}
|
|
108
134
|
// Display trusted keys
|
|
109
135
|
console.log('🔑 Trusted Signing Keys\n');
|
|
110
|
-
|
|
136
|
+
// Before the listing: an unterminated PEM block can swallow the rest of the
|
|
137
|
+
// file, so "no trusted keys" may itself be the symptom being explained.
|
|
138
|
+
(0, core_1.reportTrustedKeyProblems)(trustedProblems, trustedKeysPath);
|
|
139
|
+
if (trustedEntries.length === 0) {
|
|
111
140
|
if (!node_fs_1.default.existsSync(trustedKeysPath)) {
|
|
112
141
|
console.log('⚠️ No trusted keys file found');
|
|
113
142
|
console.log(` Location: ${trustedKeysPath}`);
|
|
@@ -117,19 +146,20 @@ function registerKeysCommand(program) {
|
|
|
117
146
|
console.log(` File exists at: ${trustedKeysPath}`);
|
|
118
147
|
}
|
|
119
148
|
console.log('\nTo add a trusted key:');
|
|
120
|
-
console.log(' dossier keys add <public-key> <identifier>\n');
|
|
149
|
+
console.log(' ai-dossier keys add <public-key> <identifier>\n');
|
|
121
150
|
if (generatedKeys.length === 0) {
|
|
122
151
|
console.log('To generate a new key pair:');
|
|
123
|
-
console.log(' dossier keys generate\n');
|
|
152
|
+
console.log(' ai-dossier keys generate\n');
|
|
124
153
|
}
|
|
125
154
|
process.exit(0);
|
|
126
155
|
}
|
|
127
|
-
console.log(`Total: ${
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
156
|
+
console.log(`Total: ${trustedEntries.length} trusted key(s)\n`);
|
|
157
|
+
trustedEntries.forEach((entry, index) => {
|
|
158
|
+
// Show the canonical form: a legacy PEM entry is multi-line and would
|
|
159
|
+
// otherwise wreck the listing.
|
|
160
|
+
const key = (0, core_1.normalizePublicKey)(entry.publicKey);
|
|
131
161
|
const shortKey = key.length > 60 ? `${key.substring(0, 60)}...` : key;
|
|
132
|
-
console.log(`${index + 1}. ${
|
|
162
|
+
console.log(`${index + 1}. ${entry.keyId}`);
|
|
133
163
|
console.log(` ${shortKey}`);
|
|
134
164
|
console.log();
|
|
135
165
|
});
|
|
@@ -139,29 +169,84 @@ function registerKeysCommand(program) {
|
|
|
139
169
|
keysCmd
|
|
140
170
|
.command('add')
|
|
141
171
|
.description('Add a trusted signing key')
|
|
142
|
-
.argument('<public-key>', 'Public key
|
|
172
|
+
.argument('<public-key>', 'Public key: raw base64, SPKI PEM (see below), or minisign')
|
|
143
173
|
.argument('<identifier>', 'Human-readable identifier (e.g., "dossier-team-2025")')
|
|
174
|
+
// A PEM begins with "-", so commander reads it as an unknown option and the
|
|
175
|
+
// action never runs. The remediation `ai-dossier verify` prints uses raw base64
|
|
176
|
+
// and avoids this, but anyone pasting a PEM or a .pub file hits it — so the
|
|
177
|
+
// escape hatch has to be visible from `--help` and from any parse error.
|
|
178
|
+
.addHelpText('after', '\nAccepted key formats:\n' +
|
|
179
|
+
' raw Ed25519 public key 44-char base64, e.g. "5kr+/8mNiy...o1M=" (canonical)\n' +
|
|
180
|
+
' SPKI PEM block "-----BEGIN PUBLIC KEY-----..."\n' +
|
|
181
|
+
' minisign public key "RWT..." (legacy)\n' +
|
|
182
|
+
'\nWhatever you pass is normalized to the raw base64 form before it is stored,\n' +
|
|
183
|
+
'so all three end up as the same entry in ~/.dossier/trusted-keys.txt.\n' +
|
|
184
|
+
'\nExamples:\n' +
|
|
185
|
+
' ai-dossier keys add 5kr+/8mNiy...o1M= "dossier-team-2025"\n' +
|
|
186
|
+
' ai-dossier keys add -- "$(cat ~/.dossier/default.pub)" "my-key"\n' +
|
|
187
|
+
'\nA PEM starts with "-", which is read as an option — put "--" before it,\n' +
|
|
188
|
+
'as in the second example. `ai-dossier verify <dossier>` prints a ready-to-run\n' +
|
|
189
|
+
'command with the key already in base64 form.\n')
|
|
190
|
+
.showHelpAfterError('Note: a PEM starts with "-" and is read as an option — use: ai-dossier keys add -- "<pem>" "<id>"')
|
|
144
191
|
.action((publicKey, identifier) => {
|
|
145
192
|
const dossierDir = node_path_1.default.join(node_os_1.default.homedir(), '.dossier');
|
|
146
193
|
const trustedKeysPath = node_path_1.default.join(dossierDir, 'trusted-keys.txt');
|
|
147
194
|
console.log('\n🔑 Adding Trusted Key\n');
|
|
195
|
+
// Store the canonical raw-key base64. A PEM argument — what `verify` used to
|
|
196
|
+
// suggest, and what a `.pub` file contains — would otherwise be appended
|
|
197
|
+
// verbatim and shredded by the line-oriented trusted-keys parser.
|
|
198
|
+
const canonicalKey = (0, core_1.normalizePublicKey)(publicKey);
|
|
199
|
+
// Validate positively rather than just rejecting whitespace. normalizePublicKey
|
|
200
|
+
// hands back anything it cannot interpret, so a typo, a truncated key, or a
|
|
201
|
+
// path to a .pub file would otherwise be written as a "trusted key" under a
|
|
202
|
+
// ✅ and only surface much later as `ai-dossier verify` saying "not trusted".
|
|
203
|
+
// KMS-signed dossiers are verified against the key ARN, not the public key,
|
|
204
|
+
// so the ARN is the only thing that can make one trusted.
|
|
205
|
+
if (!(0, core_1.isSupportedPublicKey)(canonicalKey) && !(0, core_1.isKmsKeyIdentifier)(canonicalKey)) {
|
|
206
|
+
console.error('❌ Unrecognized public key format — nothing was added');
|
|
207
|
+
console.error(` Got: ${summarizeKeyArgument(publicKey)}`);
|
|
208
|
+
console.error('\n Expected one of:');
|
|
209
|
+
console.error(' • raw Ed25519 public key — 44-char base64, e.g. "5kr+/8mNiy...o1M="');
|
|
210
|
+
console.error(' • SPKI PEM block — "-----BEGIN PUBLIC KEY-----..."');
|
|
211
|
+
console.error(' • minisign public key — "RWT..."');
|
|
212
|
+
console.error(' • AWS KMS key ARN — "arn:aws:kms:<region>:<account>:key/..."');
|
|
213
|
+
if (looksLikeFilePath(publicKey)) {
|
|
214
|
+
console.error('\n That looks like a file path. Pass the key itself, not its path:');
|
|
215
|
+
console.error(` ai-dossier keys add -- "$(cat ${publicKey})" "${identifier}"`);
|
|
216
|
+
console.error(' (the leading "--" is required: a PEM starts with "-", which the');
|
|
217
|
+
console.error(' option parser would otherwise read as a flag)');
|
|
218
|
+
}
|
|
219
|
+
console.error('\n To print a key you can paste: ai-dossier keys generate');
|
|
220
|
+
console.error(" To get a signer's key: ai-dossier verify <dossier>");
|
|
221
|
+
console.error(' (verify prints a ready-to-run `ai-dossier keys add` command)\n');
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
// The identifier is written verbatim into a line-oriented trust file, so a
|
|
225
|
+
// newline in it appends a whole extra entry — a second, unrelated key the
|
|
226
|
+
// user never approved, trusted from then on. Reject rather than escape:
|
|
227
|
+
// there is no legitimate multi-line identifier.
|
|
228
|
+
if (/[\n\r]/.test(identifier)) {
|
|
229
|
+
console.error('❌ Identifier cannot span multiple lines — nothing was added');
|
|
230
|
+
console.error(' One trusted key per line, as "<public-key> <identifier>".\n');
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
148
233
|
if (!node_fs_1.default.existsSync(dossierDir)) {
|
|
149
234
|
node_fs_1.default.mkdirSync(dossierDir, { recursive: true });
|
|
150
235
|
console.log(`✅ Created directory: ${dossierDir}`);
|
|
151
236
|
}
|
|
152
237
|
if (node_fs_1.default.existsSync(trustedKeysPath)) {
|
|
153
238
|
const content = node_fs_1.default.readFileSync(trustedKeysPath, 'utf8');
|
|
154
|
-
if (content.
|
|
239
|
+
if ((0, core_1.trustedKeysFromContent)(content).has(canonicalKey)) {
|
|
155
240
|
console.log('⚠️ This key already exists in trusted keys');
|
|
156
241
|
console.log(` Location: ${trustedKeysPath}\n`);
|
|
157
242
|
process.exit(0);
|
|
158
243
|
}
|
|
159
244
|
}
|
|
160
|
-
const entry = `${
|
|
245
|
+
const entry = `${canonicalKey} ${identifier}\n`;
|
|
161
246
|
node_fs_1.default.appendFileSync(trustedKeysPath, entry, 'utf8');
|
|
162
247
|
console.log('✅ Key added successfully');
|
|
163
248
|
console.log(` Identifier: ${identifier}`);
|
|
164
|
-
console.log(` Public Key: ${
|
|
249
|
+
console.log(` Public Key: ${canonicalKey.substring(0, 60)}${canonicalKey.length > 60 ? '...' : ''}`);
|
|
165
250
|
console.log(` Location: ${trustedKeysPath}`);
|
|
166
251
|
console.log('\nYou can now verify dossiers signed with this key.\n');
|
|
167
252
|
process.exit(0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/commands/keys.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/commands/keys.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqBzC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiazD"}
|
package/dist/commands/run.js
CHANGED
|
@@ -71,7 +71,8 @@ function registerRunCommand(program) {
|
|
|
71
71
|
let resolvedFile = file;
|
|
72
72
|
const isUrl = file.startsWith('http://') || file.startsWith('https://');
|
|
73
73
|
const isLocalFile = !isUrl && node_fs_1.default.existsSync(node_path_1.default.resolve(file));
|
|
74
|
-
const
|
|
74
|
+
const nestedHost = (0, helpers_1.detectNestedHost)();
|
|
75
|
+
const isNested = nestedHost !== null;
|
|
75
76
|
const log = isNested
|
|
76
77
|
? (...args) => console.error(...args)
|
|
77
78
|
: (...args) => console.log(...args);
|
|
@@ -239,8 +240,8 @@ function registerRunCommand(program) {
|
|
|
239
240
|
process.stderr.write(`Warning: failed to read dossier summary: ${err.message}\n`);
|
|
240
241
|
}
|
|
241
242
|
// Nested session detection
|
|
242
|
-
if (
|
|
243
|
-
console.error(
|
|
243
|
+
if (nestedHost !== null) {
|
|
244
|
+
console.error(`ℹ️ Running inside ${nestedHost} — outputting dossier content\n`);
|
|
244
245
|
const llmOption = options.llm || config.getConfig('defaultLlm') || 'auto';
|
|
245
246
|
(0, run_log_1.appendRunLog)({
|
|
246
247
|
timestamp: new Date().toISOString(),
|