WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
package skills
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Tencent/WeKnora/internal/sandbox"
|
||||
)
|
||||
|
||||
// SkillEnvResolver produces the environment one execution gets. It is separate
|
||||
// from SkillSource because the values are per-caller: the same skill, in the
|
||||
// same image, hands a different key to a different Principal. The implementation
|
||||
// lives in the service layer, which can reach the repository.
|
||||
//
|
||||
// It is keyed by skill NAME rather than id because every path into the manager
|
||||
// is name-addressed: ExecuteScript receives the name the model wrote, and the
|
||||
// row id is an implementation detail of the installed-skill source.
|
||||
type SkillEnvResolver interface {
|
||||
// ResolveEnv returns the values to inject and the names of any required
|
||||
// variable that neither the admin nor this caller has filled in. An empty
|
||||
// skillName asks for the caller's config-wide variables alone. The caller's
|
||||
// identity is taken from ctx, never from a parameter.
|
||||
ResolveEnv(ctx context.Context, skillName string) (env map[string]string, missing []string, err error)
|
||||
}
|
||||
|
||||
// MissingSkillEnvError reports that execution was refused because a required
|
||||
// variable has no value at either layer. It is typed so the agent loop can
|
||||
// relay a sentence a person can act on instead of the KeyError or 401 the
|
||||
// script would otherwise produce.
|
||||
type MissingSkillEnvError struct {
|
||||
SkillName string
|
||||
Names []string
|
||||
}
|
||||
|
||||
func (e *MissingSkillEnvError) Error() string {
|
||||
// English, like every other error in this codebase: the agent relays this
|
||||
// to the user and translates it into whatever language they are speaking.
|
||||
// execute_skill_script has no env parameter, so it cannot take a value the
|
||||
// user just typed. shell_exec can: naming the skill and passing the value
|
||||
// in env runs the command and stores the value for the next run. Pointing
|
||||
// at the settings page alone would strand IM users, who have no such page.
|
||||
return fmt.Sprintf(
|
||||
"skill %q needs the environment variable(s) %s, which nobody has set yet. "+
|
||||
"Ask the user for them, then run the skill through shell_exec with "+
|
||||
"skill_name=%q and the values in env — they are stored for that user "+
|
||||
"afterwards. They can also be set under Settings → Sandbox secrets.",
|
||||
e.SkillName, strings.Join(e.Names, ", "), e.SkillName,
|
||||
)
|
||||
}
|
||||
|
||||
// ApplyResolvedEnv overlays resolved onto env WITHOUT displacing anything env
|
||||
// already carries. It is exported because shell_exec applies the same overlay
|
||||
// for its optional skill_name parameter and must not diverge from it.
|
||||
//
|
||||
// This is the second layer of reserved-name protection. Task 2's write-time
|
||||
// blacklist is the first, but a value written before that blacklist existed
|
||||
// would still be in the database, and letting it land on
|
||||
// WEKNORA_SKILL_OUTPUT_DIR would silently redirect the turn's artifacts to a
|
||||
// directory nobody drains. Skipping existing keys makes that impossible
|
||||
// regardless of what is stored.
|
||||
func ApplyResolvedEnv(env, resolved map[string]string) {
|
||||
for name, value := range resolved {
|
||||
if _, taken := env[name]; taken {
|
||||
continue
|
||||
}
|
||||
env[name] = value
|
||||
}
|
||||
}
|
||||
|
||||
// applySessionPackagePath prepends the per-session extra-packages directory
|
||||
// so a frozen skill venv can still see `pip install --target` extras without
|
||||
// mutating the snapshot. Missing directories are ignored by the interpreters.
|
||||
func applySessionPackagePath(env map[string]string, skillName string) {
|
||||
if env == nil {
|
||||
return
|
||||
}
|
||||
dir := sandbox.SessionSkillPackageDir(skillName)
|
||||
if dir == "" {
|
||||
return
|
||||
}
|
||||
prependPathEnv(env, pythonPathEnvVar, dir)
|
||||
prependPathEnv(env, nodePathEnvVar, dir)
|
||||
}
|
||||
|
||||
func prependPathEnv(env map[string]string, key, dir string) {
|
||||
if existing := strings.TrimSpace(env[key]); existing != "" {
|
||||
env[key] = dir + ":" + existing
|
||||
return
|
||||
}
|
||||
env[key] = dir
|
||||
}
|
||||
Reference in New Issue
Block a user