owfadev 0.3.2 → 0.4.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/.agents/skills/owfa-check/SKILL.md +49 -0
- package/.agents/skills/{docs → owfa-docs}/SKILL.md +5 -5
- package/.agents/skills/{wiki_plugin_kernel → owfa-kernel}/SKILL.md +13 -12
- package/{.meta/command/plugin → .agents/skills/owfa-plugin}/SKILL.md +10 -10
- package/.agents/skills/{query → owfa-query}/SKILL.md +5 -6
- package/.owfa/VERSION +1 -0
- package/.owfa/command/owfa-check/SKILL.md +49 -0
- package/{.meta/command/docs → .owfa/command/owfa-docs}/SKILL.md +5 -5
- package/{.meta/command/wiki_plugin_kernel → .owfa/command/owfa-kernel}/SKILL.md +13 -12
- package/{.agents/skills/plugin → .owfa/command/owfa-plugin}/SKILL.md +10 -10
- package/{.meta/command/query → .owfa/command/owfa-query}/SKILL.md +5 -6
- package/{.meta → .owfa}/docs/agent-behavior.md +3 -2
- package/{.meta → .owfa}/docs/agents-example.md +1 -1
- package/{.meta → .owfa}/docs/conversion.md +2 -2
- package/{.meta → .owfa}/docs/deployment.md +51 -46
- package/{.meta → .owfa}/docs/developer.md +4 -4
- package/{.meta → .owfa}/docs/distribution.md +21 -19
- package/.owfa/docs/home.md +61 -0
- package/{.meta → .owfa}/docs/intro.md +5 -5
- package/{.meta → .owfa}/docs/log-example.md +1 -1
- package/{.meta → .owfa}/docs/map.md +4 -3
- package/{.meta → .owfa}/docs/mechanics.md +11 -11
- package/{.meta → .owfa}/docs/naming.md +7 -7
- package/{.meta → .owfa}/docs/packages.md +80 -15
- package/.owfa/docs/plugin-lifecycle.md +70 -0
- package/{.meta → .owfa}/docs/quickstart.md +12 -12
- package/{.meta → .owfa}/docs/research-user-profile.md +1 -1
- package/{.meta → .owfa}/docs/sandbox.md +3 -3
- package/{.meta → .owfa}/docs/usage.md +1 -1
- package/{.meta → .owfa}/protocol/actions.md +6 -6
- package/.owfa/protocol/registry.yaml +34 -0
- package/.owfa/scripts/owfa_home.py +273 -0
- package/{.meta/scripts/wiki_plugin_kernel.py → .owfa/scripts/owfa_kernel.py} +521 -77
- package/{.meta → .owfa}/scripts/pipeline.py +9 -9
- package/.owfa/scripts/wiki_plugin_kernel.py +15 -0
- package/{.meta → .owfa}/scripts/wikilib.py +2 -2
- package/.owfa/tests/test_capabilities.py +311 -0
- package/{.meta → .owfa}/tests/test_install_preflight.py +11 -7
- package/.owfa/tests/test_owfa_home.py +127 -0
- package/.owfa/tests/test_version_scheme.py +63 -0
- package/README.md +32 -24
- package/README.zh-CN.md +32 -24
- package/bin/owfa.js +525 -65
- package/package.json +3 -2
- package/payloads/personal/PKG.yaml +15 -0
- package/payloads/personal/README.md +7 -0
- package/{.meta/plugins/core → payloads/personal}/calendar/PLUGIN.md +1 -0
- package/{.meta/plugins/core → payloads/personal}/calendar/PLUGIN.yaml +3 -3
- package/{.agents/skills/map → payloads/personal/command/owfa-map}/SKILL.md +9 -9
- package/{.meta/command/profile → payloads/personal/command/owfa-profile}/SKILL.md +11 -11
- package/{.agents/skills/save → payloads/personal/command/owfa-save}/SKILL.md +10 -10
- package/{.meta/plugins/core → payloads/personal}/cron/PLUGIN.md +2 -0
- package/{.meta/plugins/core → payloads/personal}/cron/PLUGIN.yaml +3 -3
- package/{.meta/plugins/core → payloads/personal}/device/PLUGIN.md +1 -0
- package/{.meta/plugins/core → payloads/personal}/device/PLUGIN.yaml +3 -3
- package/{.meta/plugins/core → payloads/personal}/domain/PLUGIN.md +2 -0
- package/payloads/personal/domain/PLUGIN.yaml +8 -0
- package/{.meta/plugins/core → payloads/personal}/hot/PLUGIN.yaml +1 -1
- package/{.meta/plugins/core → payloads/personal}/index/PLUGIN.md +1 -0
- package/{.meta/plugins/core → payloads/personal}/index/PLUGIN.yaml +4 -4
- package/{.meta/plugins/core → payloads/personal}/instance-git/PLUGIN.md +5 -0
- package/{.meta/plugins/core → payloads/personal}/instance-git/PLUGIN.yaml +3 -3
- package/{.meta/plugins/core → payloads/personal}/language/PLUGIN.md +1 -0
- package/{.meta/plugins/core → payloads/personal}/language/PLUGIN.yaml +4 -4
- package/{.meta/plugins/core → payloads/personal}/link/PLUGIN.md +1 -0
- package/{.meta/plugins/core → payloads/personal}/link/PLUGIN.yaml +3 -3
- package/{.meta/plugins/core → payloads/personal}/link/scripts/check.py +1 -1
- package/{.meta/plugins/core → payloads/personal}/log/PLUGIN.md +1 -0
- package/{.meta/plugins/core → payloads/personal}/log/PLUGIN.yaml +4 -4
- package/{.meta/plugins/core → payloads/personal}/mapping/PLUGIN.yaml +1 -1
- package/{.meta/plugins/core → payloads/personal}/notes/PLUGIN.md +1 -0
- package/{.meta/plugins/core → payloads/personal}/notes/PLUGIN.yaml +4 -4
- package/{.meta/plugins/core → payloads/personal}/sessions/PLUGIN.md +2 -1
- package/{.meta/plugins/core → payloads/personal}/sessions/PLUGIN.yaml +5 -5
- package/{.meta/plugins/core → payloads/personal}/structure/PLUGIN.md +2 -0
- package/{.meta/plugins/core → payloads/personal}/structure/PLUGIN.yaml +3 -3
- package/{.meta/plugins/core → payloads/personal}/structure/scripts/check.py +1 -1
- package/{.meta/plugins/core → payloads/personal}/tag/PLUGIN.md +2 -0
- package/{.meta/plugins/core → payloads/personal}/tag/PLUGIN.yaml +3 -3
- package/{.meta/plugins/core → payloads/personal}/tag/scripts/check.py +3 -1
- package/{.meta/plugins/core → payloads/personal}/tmp/PLUGIN.md +1 -0
- package/{.meta/plugins/core → payloads/personal}/tmp/PLUGIN.yaml +3 -3
- package/{.meta/plugins/core → payloads/personal}/todo/PLUGIN.md +2 -0
- package/{.meta/plugins/core → payloads/personal}/todo/PLUGIN.yaml +6 -5
- package/payloads/personal/todo/scripts/check.py +51 -0
- package/{.meta/plugins/core → payloads/personal}/trust/PLUGIN.md +1 -0
- package/{.meta/plugins/core → payloads/personal}/trust/PLUGIN.yaml +3 -3
- package/{.meta/plugins/core → payloads/personal}/user-profile/PLUGIN.md +2 -0
- package/{.meta/plugins/core → payloads/personal}/user-profile/PLUGIN.yaml +7 -6
- package/payloads/personal/user-profile/scripts/check.py +20 -0
- package/{.meta/plugins/core → payloads/personal}/vault/PLUGIN.md +1 -0
- package/payloads/personal/vault/PLUGIN.yaml +13 -0
- package/{.meta/plugins/core → payloads/personal}/wiki/PLUGIN.md +1 -0
- package/payloads/personal/wiki/PLUGIN.yaml +8 -0
- package/templates/AGENTS.template.md +2 -2
- package/.agents/skills/check/SKILL.md +0 -130
- package/.agents/skills/profile/SKILL.md +0 -75
- package/.meta/VERSION +0 -1
- package/.meta/command/check/SKILL.md +0 -130
- package/.meta/command/map/SKILL.md +0 -72
- package/.meta/command/save/SKILL.md +0 -94
- package/.meta/plugins/core/domain/PLUGIN.yaml +0 -8
- package/.meta/plugins/core/vault/PLUGIN.yaml +0 -13
- package/.meta/plugins/core/wiki/PLUGIN.yaml +0 -8
- package/.meta/protocol/registry.yaml +0 -75
- /package/{.meta → .owfa}/command/asset-read/SKILL.md +0 -0
- /package/{.meta → .owfa}/docs/research-landscape.md +0 -0
- /package/{.meta → .owfa}/protocol/experiments.md +0 -0
- /package/{.meta/plugins/core → payloads/personal}/device/scripts/check.py +0 -0
- /package/{.meta/plugins/core → payloads/personal}/hot/PLUGIN.md +0 -0
- /package/{.meta/plugins/core → payloads/personal}/instance-git/scripts/check.py +0 -0
- /package/{.meta/plugins/core → payloads/personal}/language/scripts/check.py +0 -0
- /package/{.meta/plugins/core → payloads/personal}/log/scripts/check.py +0 -0
- /package/{.meta/plugins/core → payloads/personal}/mapping/PLUGIN.md +0 -0
- /package/{.meta/plugins/core → payloads/personal}/mapping/scripts/check.py +0 -0
- /package/{.meta/plugins/core → payloads/personal}/sessions/scripts/check.py +0 -0
- /package/{.meta/plugins/core → payloads/personal}/trust/scripts/check.py +0 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""OWFA machine substrate: connector homes and workspace bindings (work order #56, B1-B3).
|
|
4
|
+
|
|
5
|
+
Layout (machine-global — one machine, many workspaces):
|
|
6
|
+
|
|
7
|
+
~/.owfa/connectors/<name>/
|
|
8
|
+
├── bindings.json # [{id, root, home, bot?, installed_at}] — the registry
|
|
9
|
+
└── homes/<id>/ # one state dir per binding (credentials, spool, logs)
|
|
10
|
+
|
|
11
|
+
Resolution order for "which home serves this workspace":
|
|
12
|
+
|
|
13
|
+
explicit --root/--home argument
|
|
14
|
+
> OWFA_ROOT environment variable
|
|
15
|
+
> cwd reverse-lookup in the bindings registry
|
|
16
|
+
> homes/default (single-workspace machines: zero configuration)
|
|
17
|
+
|
|
18
|
+
Invariants (enforced here so every connector inherits them):
|
|
19
|
+
|
|
20
|
+
- one home, one active binding — two workspaces draining one spool is a semantic
|
|
21
|
+
error, not a health issue; `exclusivity_violations` reports it
|
|
22
|
+
- an unbound workspace is degraded-but-healthy: `resolve_home` falls back to
|
|
23
|
+
homes/default only for machines with exactly one binding-less usage pattern;
|
|
24
|
+
a workspace wanting isolation must bind explicitly
|
|
25
|
+
- unbind detaches the binding; homes/<id>/ holds data — deletion is a human act
|
|
26
|
+
- stale bindings (root directory gone) are reported by `stale_bindings` and
|
|
27
|
+
cleaned by explicit confirmation, never silently
|
|
28
|
+
|
|
29
|
+
Migration from pre-0.4 scattered homes (~/.qq-cli and friends): `migrate_legacy_home`
|
|
30
|
+
MOVES the legacy directory into homes/<id> (move, never copy — credentials must not
|
|
31
|
+
duplicated) and registers the binding.
|
|
32
|
+
|
|
33
|
+
Pure stdlib. State writes are atomic (tmp + os.replace) with 0600 perms; the
|
|
34
|
+
registry is single-writer by convention (human or installer), so no file lock.
|
|
35
|
+
"""
|
|
36
|
+
import json
|
|
37
|
+
import os
|
|
38
|
+
import re
|
|
39
|
+
import secrets
|
|
40
|
+
import sys
|
|
41
|
+
from datetime import datetime, timezone
|
|
42
|
+
from pathlib import Path
|
|
43
|
+
|
|
44
|
+
ENV_HOME = "OWFA_HOME" # machine substrate root (default ~/.owfa)
|
|
45
|
+
ENV_ROOT = "OWFA_ROOT" # workspace root (set by executors / nested sessions)
|
|
46
|
+
BINDINGS_VERSION = 1
|
|
47
|
+
_SLUG_RE = re.compile(r"[^a-z0-9]+")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def machine_root() -> Path:
|
|
51
|
+
raw = os.environ.get(ENV_HOME)
|
|
52
|
+
return Path(raw).expanduser() if raw else Path.home() / ".owfa"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def connectors_root() -> Path:
|
|
56
|
+
return machine_root() / "connectors"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def connector_dir(connector: str) -> Path:
|
|
60
|
+
return connectors_root() / connector
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def bindings_path(connector: str) -> Path:
|
|
64
|
+
return connector_dir(connector) / "bindings.json"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def homes_root(connector: str) -> Path:
|
|
68
|
+
return connector_dir(connector) / "homes"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def home_dir(connector: str, binding_id: str) -> Path:
|
|
72
|
+
return homes_root(connector) / binding_id
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _now_iso() -> str:
|
|
76
|
+
return datetime.now(timezone.utc).isoformat(timespec="seconds")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def load_bindings(connector: str) -> list:
|
|
80
|
+
try:
|
|
81
|
+
with open(bindings_path(connector), encoding="utf-8") as f:
|
|
82
|
+
data = json.load(f)
|
|
83
|
+
except (OSError, json.JSONDecodeError):
|
|
84
|
+
return []
|
|
85
|
+
out = data.get("bindings") if isinstance(data, dict) else None
|
|
86
|
+
return out if isinstance(out, list) else []
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def save_bindings(connector: str, bindings: list) -> None:
|
|
90
|
+
path = bindings_path(connector)
|
|
91
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
92
|
+
try:
|
|
93
|
+
os.chmod(path.parent, 0o700)
|
|
94
|
+
except OSError:
|
|
95
|
+
pass
|
|
96
|
+
tmp = str(path) + ".tmp"
|
|
97
|
+
with open(tmp, "w", encoding="utf-8") as f:
|
|
98
|
+
json.dump({"version": BINDINGS_VERSION, "bindings": bindings}, f, ensure_ascii=False, indent=2)
|
|
99
|
+
f.write("\n")
|
|
100
|
+
f.flush()
|
|
101
|
+
os.fsync(f.fileno())
|
|
102
|
+
try:
|
|
103
|
+
os.chmod(tmp, 0o600)
|
|
104
|
+
except OSError:
|
|
105
|
+
pass
|
|
106
|
+
os.replace(tmp, path)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _slug(root: str) -> str:
|
|
110
|
+
base = os.path.basename(os.path.normpath(root)).lower()
|
|
111
|
+
slug = _SLUG_RE.sub("-", base).strip("-")[:24] or "ws"
|
|
112
|
+
return f"{slug}-{secrets.token_hex(2)}"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def resolve_root(connector: str, explicit: str = "") -> str:
|
|
116
|
+
"""Explicit argument > OWFA_ROOT env > cwd reverse-lookup > '' (unresolved → default)."""
|
|
117
|
+
if explicit:
|
|
118
|
+
return str(Path(explicit).expanduser().resolve())
|
|
119
|
+
env = os.environ.get(ENV_ROOT)
|
|
120
|
+
if env:
|
|
121
|
+
return str(Path(env).expanduser().resolve())
|
|
122
|
+
cwd = os.getcwd()
|
|
123
|
+
for b in load_bindings(connector):
|
|
124
|
+
try:
|
|
125
|
+
root = str(Path(b.get("root", "")).resolve())
|
|
126
|
+
except OSError:
|
|
127
|
+
continue
|
|
128
|
+
if root and (cwd == root or cwd.startswith(root + os.sep)):
|
|
129
|
+
return root
|
|
130
|
+
return ""
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def resolve_home(connector: str, explicit_root: str = "", explicit_home: str = "") -> Path:
|
|
134
|
+
"""The home directory serving the resolved workspace (B1: homes/<id>, default = zero-config)."""
|
|
135
|
+
if explicit_home:
|
|
136
|
+
return Path(explicit_home).expanduser()
|
|
137
|
+
root = resolve_root(connector, explicit_root)
|
|
138
|
+
bindings = load_bindings(connector)
|
|
139
|
+
for b in bindings:
|
|
140
|
+
if root and os.path.normpath(b.get("root", "")) == os.path.normpath(root):
|
|
141
|
+
return Path(b["home"])
|
|
142
|
+
if not root and len(bindings) == 1:
|
|
143
|
+
return Path(b["home"]) # single binding, no better signal — it is the default
|
|
144
|
+
if len(bindings) >= 2:
|
|
145
|
+
# multi-binding machine, workspace matches nothing: default would be an
|
|
146
|
+
# unregistered home that two unmatched workspaces could silently share
|
|
147
|
+
# (review round 3) — degraded-but-healthy, but it must be loud
|
|
148
|
+
print(
|
|
149
|
+
f"[owfa-home] warning: workspace matches none of {len(bindings)} bindings — "
|
|
150
|
+
"serving homes/default; bind this root or set OWFA_ROOT to remove the ambiguity",
|
|
151
|
+
file=sys.stderr,
|
|
152
|
+
)
|
|
153
|
+
return home_dir(connector, "default")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def bind(connector: str, root: str, binding_id: str = "", bot: str = "") -> dict:
|
|
157
|
+
"""Register a workspace binding (B2). Re-binding the same root replaces the entry."""
|
|
158
|
+
root = str(Path(root).expanduser().resolve())
|
|
159
|
+
bindings = load_bindings(connector)
|
|
160
|
+
entry = {
|
|
161
|
+
"id": binding_id or next((b["id"] for b in bindings if os.path.normpath(b.get("root", "")) == os.path.normpath(root)), ""),
|
|
162
|
+
"root": root,
|
|
163
|
+
"home": str(home_dir(connector, binding_id or "")) if binding_id else "",
|
|
164
|
+
"bot": bot,
|
|
165
|
+
"installed_at": _now_iso(),
|
|
166
|
+
}
|
|
167
|
+
if not entry["id"]:
|
|
168
|
+
entry["id"] = "default" if not bindings else _slug(root)
|
|
169
|
+
entry["home"] = str(home_dir(connector, entry["id"]))
|
|
170
|
+
bindings = [b for b in bindings if b.get("id") != entry["id"]]
|
|
171
|
+
bindings.append(entry)
|
|
172
|
+
save_bindings(connector, bindings)
|
|
173
|
+
Path(entry["home"]).mkdir(parents=True, exist_ok=True, mode=0o700) # docs/home.md: 0600/0700
|
|
174
|
+
return entry
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def unbind(connector: str, binding_id: str) -> bool:
|
|
178
|
+
"""Detach a binding. homes/<id>/ keeps its data — deletion belongs to the human (B3)."""
|
|
179
|
+
bindings = load_bindings(connector)
|
|
180
|
+
kept = [b for b in bindings if b.get("id") != binding_id]
|
|
181
|
+
if len(kept) == len(bindings):
|
|
182
|
+
return False
|
|
183
|
+
save_bindings(connector, kept)
|
|
184
|
+
return True
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def exclusivity_violations(connector: str) -> list:
|
|
188
|
+
"""Homes served by more than one active binding (B3: this is an error, not a warning)."""
|
|
189
|
+
seen = {}
|
|
190
|
+
for b in load_bindings(connector):
|
|
191
|
+
seen.setdefault(b.get("home", ""), []).append(b.get("id", ""))
|
|
192
|
+
return [{"home": h, "bindings": ids} for h, ids in sorted(seen.items()) if len(ids) > 1]
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def stale_bindings(connector: str) -> list:
|
|
196
|
+
"""Bindings whose workspace root disappeared — report and clean on confirmation (B3)."""
|
|
197
|
+
return [b for b in load_bindings(connector) if not os.path.isdir(b.get("root", ""))]
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def migrate_legacy_home(connector: str, legacy_dir: str, binding_id: str = "", root: str = "") -> dict:
|
|
201
|
+
"""Move a pre-0.4 scattered home (e.g. ~/.qq-cli) into homes/<id> and bind it.
|
|
202
|
+
|
|
203
|
+
MOVE, never copy: credentials must not exist in two places. The legacy
|
|
204
|
+
directory is left as a symlink to the new home so old absolute paths keep
|
|
205
|
+
resolving — the symlink is the 0.5-cleanup item.
|
|
206
|
+
"""
|
|
207
|
+
src = Path(legacy_dir).expanduser()
|
|
208
|
+
if not src.exists() and not src.is_symlink():
|
|
209
|
+
print(f"[owfa-home] warning: legacy home {src} does not exist — registering the binding only")
|
|
210
|
+
entry = bind(connector, root or os.getcwd(), binding_id) # empty id → reuse the root's existing binding
|
|
211
|
+
dst = Path(entry["home"])
|
|
212
|
+
if src.is_symlink():
|
|
213
|
+
return entry # already migrated
|
|
214
|
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
215
|
+
if src.exists():
|
|
216
|
+
# bind() pre-creates homes/<id>; POSIX rename replaces an empty existing
|
|
217
|
+
# directory, Windows raises WinError 183 — clear the empty pre-creation
|
|
218
|
+
# first so the move works on both (review P1, PR #57 round 2).
|
|
219
|
+
if dst.is_dir():
|
|
220
|
+
if any(dst.iterdir()):
|
|
221
|
+
raise SystemExit(
|
|
222
|
+
f"[owfa-home] refusing to migrate: target home {dst} already exists and is not empty"
|
|
223
|
+
)
|
|
224
|
+
dst.rmdir()
|
|
225
|
+
os.rename(src, dst) # same volume — atomic enough
|
|
226
|
+
try:
|
|
227
|
+
if os.name == "nt":
|
|
228
|
+
import _winapi
|
|
229
|
+
|
|
230
|
+
# Junctions need no admin rights / Developer Mode, unlike
|
|
231
|
+
# os.symlink on Windows. Argument order matches os.symlink:
|
|
232
|
+
# junction AT src pointing TO dst.
|
|
233
|
+
_winapi.CreateJunction(str(dst), str(src))
|
|
234
|
+
else:
|
|
235
|
+
os.symlink(dst, src, target_is_directory=True)
|
|
236
|
+
except OSError as exc:
|
|
237
|
+
print(f"[owfa-home] warning: could not link {src} -> {dst} ({exc}); old absolute paths will not resolve")
|
|
238
|
+
return entry
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
if __name__ == "__main__":
|
|
242
|
+
import argparse
|
|
243
|
+
|
|
244
|
+
ap = argparse.ArgumentParser(description="OWFA machine substrate: connector home bindings")
|
|
245
|
+
sub = ap.add_subparsers(dest="cmd", required=True)
|
|
246
|
+
for name in ("bind", "unbind", "resolve", "list", "stale", "exclusive", "migrate"):
|
|
247
|
+
s = sub.add_parser(name)
|
|
248
|
+
s.add_argument("connector")
|
|
249
|
+
if name in ("bind", "resolve", "migrate"):
|
|
250
|
+
s.add_argument("--root", default="")
|
|
251
|
+
if name in ("bind", "migrate"):
|
|
252
|
+
s.add_argument("--id", default="")
|
|
253
|
+
s.add_argument("--bot", default="")
|
|
254
|
+
if name == "migrate":
|
|
255
|
+
s.add_argument("--legacy", required=True)
|
|
256
|
+
if name == "unbind":
|
|
257
|
+
s.add_argument("binding_id")
|
|
258
|
+
args = ap.parse_args()
|
|
259
|
+
c = args.connector
|
|
260
|
+
if args.cmd == "bind":
|
|
261
|
+
print(json.dumps(bind(c, args.root or os.getcwd(), args.id, args.bot), ensure_ascii=False))
|
|
262
|
+
elif args.cmd == "unbind":
|
|
263
|
+
print("unbound" if unbind(c, args.binding_id) else "no such binding")
|
|
264
|
+
elif args.cmd == "resolve":
|
|
265
|
+
print(resolve_home(c, args.root))
|
|
266
|
+
elif args.cmd == "list":
|
|
267
|
+
print(json.dumps(load_bindings(c), ensure_ascii=False, indent=2))
|
|
268
|
+
elif args.cmd == "stale":
|
|
269
|
+
print(json.dumps(stale_bindings(c), ensure_ascii=False))
|
|
270
|
+
elif args.cmd == "exclusive":
|
|
271
|
+
print(json.dumps(exclusivity_violations(c), ensure_ascii=False))
|
|
272
|
+
elif args.cmd == "migrate":
|
|
273
|
+
print(json.dumps(migrate_legacy_home(c, args.legacy, args.id, args.root or os.getcwd()), ensure_ascii=False))
|