chore: import upstream snapshot with attribution
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / results (push) Has been cancelled
CI / deadcode (push) Has been cancelled
CI / e2e-live (push) Has been cancelled
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / results (push) Has been cancelled
CI / deadcode (push) Has been cancelled
CI / e2e-live (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
audit-observer/audit-observer
|
||||
readonly-policy/readonly-policy
|
||||
@@ -0,0 +1,13 @@
|
||||
# lark-cli plugin examples
|
||||
|
||||
Runnable fork-and-blank-import examples that demonstrate the Plugin
|
||||
SDK in production-shape. Each subdirectory is a complete `main`
|
||||
package: `go build .` produces a working CLI.
|
||||
|
||||
| Example | What it shows |
|
||||
| --- | --- |
|
||||
| [audit-observer](./audit-observer/) | Simplest possible plugin: one Observer matching every command, logs to stderr. |
|
||||
| [readonly-policy](./readonly-policy/) | Policy plugin: `Restrict()` with `MaxRisk=read`, demonstrates the `FailClosed` + `Restricts=true` auto-pairing. |
|
||||
|
||||
All examples are built by CI (`make examples-build`) so they cannot
|
||||
silently drift from the SDK.
|
||||
@@ -0,0 +1,26 @@
|
||||
# Example: audit observer
|
||||
|
||||
The simplest possible lark-cli plugin: one After observer that logs
|
||||
every dispatched command to stderr (success or failure).
|
||||
|
||||
## Build & run
|
||||
|
||||
```sh
|
||||
cd extension/platform/examples/audit-observer
|
||||
go build -o audit-cli .
|
||||
./audit-cli config plugins show
|
||||
# {"plugins":[{"name":"audit", ...}], "total":1}
|
||||
|
||||
./audit-cli api GET /open-apis/contact/v3/users/me
|
||||
# [audit] api ok (on stderr)
|
||||
```
|
||||
|
||||
## Key points
|
||||
|
||||
- `platform.NewPlugin(...).MustBuild()` from `init()`. The blank
|
||||
import of this package in `main.go` triggers `init()`.
|
||||
- `Observer(platform.After, ...)` runs **after** the command's RunE,
|
||||
even on failure (Observers cannot prevent execution).
|
||||
- `FailOpen()` means: if Install ever fails, the binary logs a
|
||||
warning and continues without this plugin. Right default for
|
||||
audit-only plugins.
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Command audit-observer is a runnable fork of lark-cli that logs
|
||||
// every dispatched command to stderr. Demonstrates the simplest
|
||||
// possible plugin: one After observer matching All commands.
|
||||
//
|
||||
// Build & run:
|
||||
//
|
||||
// cd extension/platform/examples/audit-observer
|
||||
// go build -o audit-cli .
|
||||
// ./audit-cli config plugins show # see "audit" in the list
|
||||
// ./audit-cli api GET /open-apis/... # observer logs to stderr
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/larksuite/cli/cmd"
|
||||
"github.com/larksuite/cli/extension/platform"
|
||||
)
|
||||
|
||||
func init() {
|
||||
platform.Register(
|
||||
platform.NewPlugin("audit", "0.1.0").
|
||||
Observer(platform.After, "log", platform.All(),
|
||||
func(ctx context.Context, inv platform.Invocation) {
|
||||
path := inv.Cmd().Path()
|
||||
if err := inv.Err(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "[audit] %s FAILED: %v\n", path, err)
|
||||
} else {
|
||||
log.Printf("[audit] %s ok", path)
|
||||
}
|
||||
}).
|
||||
FailOpen().
|
||||
MustBuild())
|
||||
}
|
||||
|
||||
func main() {
|
||||
os.Exit(cmd.Execute())
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
# Example: read-only policy
|
||||
|
||||
A policy plugin that installs a `Rule` allowing only `docs/*` and
|
||||
`im/*` read commands. Any write command produces a structured
|
||||
`command_denied` envelope.
|
||||
|
||||
## Build & run
|
||||
|
||||
```sh
|
||||
cd extension/platform/examples/readonly-policy
|
||||
go build -o readonly-cli .
|
||||
|
||||
./readonly-cli config policy show
|
||||
# {
|
||||
# "source": "plugin",
|
||||
# "source_name": "readonly",
|
||||
# "denied_paths": N,
|
||||
# "rule": {
|
||||
# "name": "agent-readonly",
|
||||
# "allow": ["docs/**", "im/**"],
|
||||
# "deny": [],
|
||||
# "max_risk": "read",
|
||||
# "identities": [],
|
||||
# "allow_unannotated": false
|
||||
# }
|
||||
# }
|
||||
|
||||
./readonly-cli docs +update --doc-token X --content Y
|
||||
# {"ok":false,"error":{
|
||||
# "type":"command_denied",
|
||||
# "detail":{
|
||||
# "layer":"policy",
|
||||
# "policy_source":"plugin:readonly",
|
||||
# "rule_name":"agent-readonly",
|
||||
# "reason_code":"write_not_allowed"
|
||||
# }
|
||||
# }}
|
||||
|
||||
./readonly-cli docs +fetch --doc-token X
|
||||
# Normal read response (assuming credentials)
|
||||
```
|
||||
|
||||
## Key points
|
||||
|
||||
- `Restrict(&Rule{...})` is the only call needed — the Builder
|
||||
flips Capabilities to `Restricts=true, FailurePolicy=FailClosed`
|
||||
automatically. A policy plugin that silently fails to install
|
||||
would erase the security boundary, so FailClosed is enforced.
|
||||
- `MaxRisk: platform.RiskRead` rejects any command annotated
|
||||
write / high-risk-write.
|
||||
- `AllowUnannotated` is left default (false): unannotated commands
|
||||
are denied with `risk_not_annotated`. Set it to true if you need
|
||||
a gradual-adoption window for the lark-cli main tree.
|
||||
|
||||
## Caveats
|
||||
|
||||
- A binary may have **only one** plugin calling `Restrict()`. Two
|
||||
policy plugins is a deliberate `plugin_conflict` configuration
|
||||
error.
|
||||
- This Rule shadows any `~/.lark-cli/policy.yml` — plugin Rule
|
||||
wins per the resolver precedence.
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Command readonly-policy is a runnable fork of lark-cli that
|
||||
// installs a Rule permitting only docs/* and im/* read commands.
|
||||
// Any write command produces a structured command_denied envelope.
|
||||
//
|
||||
// Build & run:
|
||||
//
|
||||
// cd extension/platform/examples/readonly-policy
|
||||
// go build -o readonly-cli .
|
||||
// ./readonly-cli docs +update --doc-token X --content Y
|
||||
// # {"ok":false,"error":{"type":"command_denied", ...}}
|
||||
//
|
||||
// ./readonly-cli config policy show
|
||||
// # shows the active Rule with source=plugin:readonly
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/larksuite/cli/cmd"
|
||||
"github.com/larksuite/cli/extension/platform"
|
||||
)
|
||||
|
||||
func init() {
|
||||
platform.Register(
|
||||
platform.NewPlugin("readonly", "0.1.0").
|
||||
Restrict(&platform.Rule{
|
||||
Name: "agent-readonly",
|
||||
Description: "Only read-class docs/im commands. Suitable for AI-agent sessions.",
|
||||
Allow: []string{"docs/**", "im/**"},
|
||||
MaxRisk: platform.RiskRead,
|
||||
// AllowUnannotated stays default false (fail-closed):
|
||||
// unannotated commands are denied, surfacing missing
|
||||
// risk_level annotations early in adoption.
|
||||
}).
|
||||
MustBuild())
|
||||
// Note: Restrict() implicitly sets Restricts=true and FailClosed.
|
||||
// No need to call FailClosed() explicitly.
|
||||
}
|
||||
|
||||
func main() {
|
||||
os.Exit(cmd.Execute())
|
||||
}
|
||||
Reference in New Issue
Block a user