Drop the sks_signer build tag, mirroring the darwin keychain signer: the
TPM signer now compiles into every linux and windows/amd64 build via
constraint //go:build linux || (windows && amd64) — no -tags needed.
windows/arm64 is arch-excluded (go-ole has no arm64 VARIANT) and falls
back to client_secret only.
- goreleaser: drop -tags=sks_signer; merge windows-arm64 into the windows
build (amd64+arm64) since no tag is needed and arm64 is arch-excluded.
- build-pkg-pr-new.sh: remove tag logic.
- doctor: update the no-signer hint (signer ships by default on macOS,
Linux, Windows/amd64).
- Switching from a custom tag to GOOS/GOARCH constraints also lets
go mod tidy track sks/go-tpm/go-ole correctly.
The keychain signer lacked a HardwareProber, so probeHardware() returned
ok=false and doctor printed "no TEE signer in this build" on macOS — a
false negative, since the signer is registered and private_key_jwt works.
Implement ProbeHardware on keychainSigner (reports backend=keychain,
available when /usr/bin/security is present; no key access, no prompt) so
doctor shows 'keychain TEE available'.
sks's Windows COM dependency go-ole v1.2.5 has no arm64 VARIANT, so
building windows/arm64 with -tags sks_signer fails (undefined: VARIANT).
Mirror .goreleaser.yml's windows-arm64 build: ship arm64 without the TPM
signer (client_secret only). Other targets keep sks_signer.
- init_interactive.go: use errors.Is(err, huh.ErrUserAborted) instead of ==
(the new auth-method picker path; comparison fails on wrapped errors)
- app_registration.go: wrap read-body error with %w instead of %v
Drop the keychain_signer build tag now that the signer is cgo-free
(purego runtime FFI). darwin builds always include it, so release and
PR-preview binaries are signed without a tag. Adjust go vet to
-unsafeptr=false for the FFI data-symbol dereference (golangci-lint
still runs full govet honoring the inline //nolint:govet).
Support content_v2 post message conversion in IM shortcuts so newer post payloads render with the expected markdown, mention, and image formats while preserving fallback compatibility with legacy content.
Previously, im.message.receive events with message_type: interactive surfaced the raw JSON
payload as content, requiring callers to manually parse the card schema. This PR introduces a
user_dsl renderer (ConvertInteractiveEventContent) that converts interactive card content into
structured human-readable text — consistent with how text, post, image, and other message
types are already handled.
The output format is <card title="..." subtitle="...">...</card>, with each card element type
serialised to a readable representation (markdown body, button links, table rows, chart summaries,
etc.).
* feat(okr): add +batch-create, +reorder, +weight shortcuts
Add three new OKR shortcuts for managing objectives and key results:
- +batch-create: Bulk create objectives with key results, with automatic
rollback on failure
- +reorder: Adjust position of objectives or key results within a cycle/objective
- +weight: Adjust weights of objectives or key results with automatic
normalization using fixed-point arithmetic to avoid float precision issues
Key implementation details:
- API paths use underscore separators (/objectives_position, /objectives_weight)
- Weight normalization uses json.Number for precise JSON serialization
- Items are sorted by position before API calls to match backend requirements
- Full unit test coverage and dry-run/live E2E tests
- Skill documentation with usage examples and parameter descriptions
Change-Id: I92b658e0cc42ffa8cbdaec2ec628a079bcfc38f5
* fix: skill simplify & minor fix
Change-Id: I3f27a01cdae2122f26e48ee2acb7f334f2bab7d2
* fix: CR issue
Change-Id: Id9fab84e06f0d67e9f79c1fb9946b6b633200592
* fix: CR issue 2
Change-Id: I6a5e57dd4b10dc79f8681ec614354fbba82abc04
* fix: error handle of +weight shortcut
Change-Id: I6e2a39269e62e3b504e681110843b2ccc315a527
Replace the cgo Security.framework bindings with runtime FFI (ebitengine/purego)
so the keychain_signer builds with CGO_ENABLED=0 and cross-compiles for darwin
from any host. Same non-extractable-key security model (SecKeyCreateSignature on
an OS-held key). Release goes back to a single ubuntu runner; a macos-latest job
validates the FFI round-trip on real hardware as a release gate.
* refactor: retire legacy error envelopes and enforce typed contract
Consolidate all command error reporting onto the typed errs.* contract, remove
the legacy error surface that predated it, and tighten the lint guards so the
contract holds across the whole repository going forward.
Every failure now reaches stderr as one envelope shape: a category, an
optional subtype, a human- and agent-readable message, and a recovery hint,
with invalid parameters listed under `params`. The legacy ExitError envelope,
its constructors, and the boundary bridge that promoted untyped config and
authorization errors are deleted, leaving a single path from error to wire.
Predicate commands keep their silent-exit behavior through a dedicated signal
that carries only an exit code.
Infrastructure paths that still emitted ad-hoc envelopes — flag parsing,
unknown commands and subcommands, plugin and policy guards, confirmation
prompts, and auth/config failures — now classify into the same taxonomy.
Business, API, auth, and config exit codes are preserved; the one behavioral
change is that Cobra usage failures (missing required flag, unknown command,
bad arguments) now emit the typed validation envelope and exit 2, matching the
explicit flag and subcommand guards, instead of Cobra's plain-text exit 1.
Enforcement is repo-wide rather than per-path:
- The errscontract guards run by default everywhere instead of through a
migration allowlist, so legacy envelopes cannot be reintroduced anywhere.
- errorlint runs across the whole repository: every error wrap must use %w and
every comparison must use errors.Is/errors.As, so interior wraps stay legal
but can no longer break the chain the typed boundary relies on.
- The errs-no-bare-wrap guard is keyed by structural prefix instead of an
explicit per-domain allowlist, so new shortcut domains are covered without
editing a list. It runs where forbidigo is enabled (the shortcut domains and
the auth/config/service command groups); repo-wide chain integrity for the
remaining command paths is carried by errorlint above.
* test: align cli_e2e success assertions to the ok envelope
The api and service success path now emits the {"ok":true} envelope, so the
cli_e2e workflow assertions that still expected the old {"code":0} shape via
AssertStdoutStatus(t, 0) fail once they run with live credentials. Switch those
workflow assertions to AssertStdoutStatus(t, true); the fake-payload helper test
in core_test.go keeps its code-shape assertion.
When --name is omitted, remind user that the title defaults to the source
filename and may duplicate content headings, causing visual redundancy.
Ask whether to rename before executing the import.
private_key_jwt shipped with only a macOS Keychain signer, so
keysigner.Active() was nil on Linux/Windows and the secretless auth was
unusable there (registration failed with "requires a TEE key signer").
Add a TPM 2.0 signer backed by github.com/facebookincubator/sks — the
backend named in the keysigner docstring — behind the `sks_signer` build
tag, mirroring the macOS `keychain_signer` gating.
Signer (extension/keysigner/signer_sks.go, (linux||windows) && sks_signer):
- Non-exportable ECDSA P-256 key in the TPM (/dev/tpmrm0 on Linux, CNG on
Windows); ES256.
- sks emits ASN.1 DER but JWS requires fixed-width r||s (RFC 7518 §3.4);
add ecdsaDERToJOSE in the core and convert. Both sks backends emit DER.
- EnsureKey creates-or-loads, PublicKey reads without creating, Sign
hashes+signs+converts.
- Silence sks's verbose flog (glog-fork) TPM logging in init() via
flog.SetOutput(io.Discard); the CLI does not use flog and real failures
are returned as errors.
TEE diagnostics:
- HardwareProber capability + ProbeActiveHardware in the core; sksSigner
implements it via sks.GetSecureHardwareVendorData (prefix-collapsed error
text).
- `lark-cli doctor` gains a tee_signer check: a hard requirement for
private_key_jwt apps, informational for client_secret.
- doctor renders a human-readable report on a TTY and keeps JSON for
pipes/scripts; add IOStreams.StdoutIsTerminal (stdout-based, unlike the
stdin-based IsTerminal) so `doctor | jq` still emits JSON.
Dependency: pin sks to its last go-1.20 commit (6823f23, before sks bumped
its own go directive to 1.24) so the CLI module stays on go 1.23 and the
golang.org/x/* packages are not force-upgraded. sks pulls a pure-Go TPM
stack, compiled only under -tags sks_signer, so the default build stays
free of it (client_secret only).
Verified on linux/amd64 against a real TPM 2.0: key creation, ES256 signing
with r||s verification, and the full private_key_jwt registration +
tenant-token mint via TPM-signed client_assertion.