#150 added the report-outcomes docs to the README (which ships in the npm
package) and the create-payment-credential skill, but merged without a
changeset. Add a patch changeset so the next release publishes the updated
README.
Committed-By-Agent: claude
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* docs: add report command instructions to SKILL.md and README
- SKILL.md: reporting instructions (when to report, tag reference, examples)
- README.md: report outcomes section with usage examples
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
* docs: make outcome reporting opt-in and add tag guidance
Address review feedback on the reporting docs:
- Soften the "REQUIRED" / "always report" language to encouraged-but-optional
in both SKILL.md and README, so reporting is suggested rather than mandated
for now.
- Add guidance to prefer the most specific tag and use `other` only when none
apply (describing details in --freeform-context).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Committed-By-Agent: claude
* docs: reword report rationale to "improve checkout for agents"
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Committed-By-Agent: claude
* docs: align SKILL.md report rationale wording with README
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Committed-By-Agent: claude
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add report command for agent observability
Add `link-cli report` command for reporting purchase outcomes (success,
blocked, abandoned) after every agent purchase attempt.
- packages/cli/src/commands/report/schema.ts — Zod schema using SDK constants
- packages/cli/src/commands/report/index.tsx — Command definition
- packages/cli/src/utils/resource-factory.ts — Add createReportResource()
- packages/cli/src/cli.tsx — Register the command
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Committed-By-Agent: claude
* fix: register report as a leaf command so CLI flags parse
The report command was registered with cli.command('', {...}) under a
router Cli. The empty-string subcommand name broke flag parsing
(`link-cli report --domain ...` treated --domain as a subcommand) and
produced the MCP tool name `report_`. Switch to the leaf-command form
used by demo/onboard — pass options/run directly to Cli.create — and
gate auth with requireAuthGuard inside run, matching spend-request.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Committed-By-Agent: claude
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add IReportResource interface and ReportResource implementation that
POSTs to /agent_observations. Supports reporting purchase outcomes
(success, blocked, abandoned) with domain, tags, and context.
* Upload SEAs to release artifacts as well
* Updates to SEA Build config
* Fixes to get the SEA builds working
* SEA-specific build config
* revert unneeded changes
* feat(sdk): migrate WebBotAuthResource to POST /web_bot_auth/sign
PR #2208449 (pay-server) added a dedicated signing endpoint. The
credentials endpoint no longer accepts the url parameter or returns
web_bot_auth. Update the SDK resource to call the dedicated endpoint.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
* code review comments
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
I've added retry mechanism, 4 retries with 15min delay per retry and if retry maxed out it will ping the Slack channel. The code has been tested on my private test repo.
* feat: add serve command to expose MCP endpoint over HTTP
Committed-By-Agent: claude
* fix: cleanup and readme
* Potential fix for pull request finding 'CodeQL / Information exposure through a stack trace'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
---------
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
* feat: add spend-request list command
Implements `spend-request list` subcommand that calls GET /spend_requests,
unwraps the response, and renders active spend requests grouped by status
with color coding. Includes SDK interface update, implementation, Ink UI
component, subcommand registration, and unit/integration tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
* fix: correct onComplete type in SpendRequestList to accept null
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
* fix: apply biome formatting to list command files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
* fix: call exit() after list completes so interactive mode terminates
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
* fix: apply biome formatting to useAsyncAction destructure in list.tsx
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add noRefresh option to createAccessTokenProvider
Committed-By-Agent: claude
* feat: add env-based token provider to ResourceFactory
Committed-By-Agent: claude
* feat: read LINK_ACCESS_TOKEN, LINK_REFRESH_TOKEN, LINK_NO_REFRESH from env
Committed-By-Agent: claude
* docs: add LINK_ACCESS_TOKEN, LINK_REFRESH_TOKEN, LINK_NO_REFRESH to env vars docs
Committed-By-Agent: claude
* fix: auth status shows LINK_ACCESS_TOKEN state instead of stored credentials
Committed-By-Agent: claude
* refactor: extract resolveAuthInfo helper, simplify auth status component
Committed-By-Agent: claude
* fix: formatting
* security: refuse to write credentials through pre-existing symlinks
writeCredentialFile (cli/src/utils/credential-output.ts) was vulnerable to
a TOCTOU exfiltration on shared filesystems. The previous flow was:
fs.access(resolved) // follows symlinks
fs.writeFile(resolved, ..., { mode: 0o600 }) // follows symlinks; mode
// applied only if file is
// created, not if exists
fs.chmod(resolved, 0o600) // follows symlinks; sets perms on TARGET
If the operator runs `spend-request retrieve <id> --output-file <path>
--force` and an attacker on the same filesystem (CI runner, multi-tenant
host, container with shared /tmp) pre-plants a symlink at <path> pointing
at a file the attacker can already read, the attacker:
1. Plants <path> as a symbolic link to /tmp/<readable>.
2. Opens a read fd on /tmp/<readable> while it is world-readable.
3. Operator runs the command: writeFile follows the symlink and writes
the full card credential (PAN, CVV, billing address, valid_until) to
the symlink target.
4. Operator's chmod 0o600 then locks the target down — too late, the
attacker's fd was opened before the chmod and survives it.
Verified end-to-end by a Node reproduction: a fresh fd opened against the
target before the operator's writeCredentialFile call reads the JSON-
encoded credential immediately after the call returns, regardless of the
chmod that follows.
Fix replaces fs.access + fs.writeFile + fs.chmod with a single
fs.open(resolved, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW, 0o600). The
mode is set at create time. O_NOFOLLOW makes open fail with ELOOP if the
final path component is a symlink. O_EXCL makes open fail with EEXIST if
the file already exists. Force-mode unlinks any pre-existing entry first
(operating on the symlink itself via fs.unlink, not the target via
fs.writeFile), then takes the same atomic-create path.
Tests added to credential-output.test.ts:
- refuses to write through a symbolic link without force
- refuses to write through a symbolic link with force (target untouched)
- TOCTOU race-fail-closed
- 0o600 mode produced even with --force (regression guard for the
removed fs.chmod step)
`pnpm test` is green (117/117 across 11 files). Negative control: with
the implementation reverted, two of the new tests fail because the
symlink target is overwritten by the credential JSON.
* security(credential-output): scope POSIX-only protection explicitly
Addresses review feedback on PR #94. Gates O_NOFOLLOW as
constants.O_NOFOLLOW ?? 0 with an inline note on the Windows gap
(O_EXCL still prevents overwriting a pre-existing entry, but the path
no longer provides full no-follow protection there). Trims in-file
comment; the full attack chain stays in the PR body and tests. Drops
the biome-ignore directive that pointed at a non-existent rule.
* security(credential-output): refuse symlinks even with --force
Addresses review feedback on PR #94. The previous revision unlinked any
pre-existing entry in --force mode and then ran the atomic open with
O_EXCL | O_NOFOLLOW. That cleared the path for the create but also
silently destroyed pre-existing symlinks, and the no-follow guarantee
only covered the race window after the unlink.
This refactor inverts the precheck: lstat first, refuse outright when
the final path is a symlink (with or without --force), and only unlink
and recreate for non-symlink existing files. O_EXCL | O_NOFOLLOW remains
the race defense between the precheck and the atomic create.
Tests updated. The without-force symlink case now expects
OUTPUT_FILE_SYMLINK and asserts the symlink survives. The with-force
case is renamed to make the new contract obvious and gets the same
assertions. The dedicated post-unlink-race test is dropped because its
premise (force unlinks the symlink) no longer applies; the regular-file
race defense is exercised by the existing 0o600 test.
CI builds, typechecks, runs biome and pnpm tests, and finally
`pnpm publish --dry-run`. None of these reach beyond the working
tree. Declaring contents:read at workflow scope makes that intent
explicit and matches the workflow-level permissions blocks that
release.yml and issue-triggered-create-jira.yml already carry.
Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
* support polling straight from auth login
* improvements
* sanitize output; make the polling into a shared helper and share across the auth login polling and the auth status polling