nawabari 0.5.0 → 0.6.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/README.md +41 -13
- package/dist/cli.js +121 -21
- package/dist/cli.js.map +1 -1
- package/dist/contract.js +56 -0
- package/dist/contract.js.map +1 -1
- package/dist/domain/session-backend.d.ts +2 -1
- package/dist/domain/session-backend.js +46 -12
- package/dist/domain/session-backend.js.map +1 -1
- package/dist/domain/session.d.ts +26 -0
- package/dist/domain/session.js +3 -0
- package/dist/domain/session.js.map +1 -1
- package/dist/session-registry.d.ts +53 -8
- package/dist/session-registry.js +280 -19
- package/dist/session-registry.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -94,6 +94,7 @@ The supported standalone sequence is:
|
|
|
94
94
|
```text
|
|
95
95
|
session create -> session claim(s) -> authorize/checkpoint
|
|
96
96
|
-> commit/push -> doctor (reconciliation) -> session close/gc
|
|
97
|
+
-> explicit session discard only when the selected work is intentionally disposable
|
|
97
98
|
```
|
|
98
99
|
|
|
99
100
|
The JSON envelope is one document on stdout. Success has `ok: true`, a
|
|
@@ -106,16 +107,17 @@ failure. Consumers must use these fields and codes, never human presentation.
|
|
|
106
107
|
|
|
107
108
|
The result schemas expose the following identities:
|
|
108
109
|
|
|
109
|
-
| Surface | Versioned identities
|
|
110
|
-
| ---------------------- |
|
|
111
|
-
| session lifecycle | `session_id`, `repository`, `worktree`, `branch`, `state`
|
|
112
|
-
|
|
|
113
|
-
|
|
|
114
|
-
|
|
|
115
|
-
|
|
|
116
|
-
|
|
|
117
|
-
|
|
|
118
|
-
|
|
|
110
|
+
| Surface | Versioned identities |
|
|
111
|
+
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
112
|
+
| session lifecycle | `session_id`, `repository`, `worktree`, `branch`, `state` |
|
|
113
|
+
| explicit discard | `previous_head`, `worktree_removed`, `branch_removed`, `released_claims`, `final_state`, final `session.state`/`terminal_operation` |
|
|
114
|
+
| claims | `claim_id`, `session_id`, `resource`, `mode` |
|
|
115
|
+
| authorization | `operation`, `allowed`, `code`, `claim_ids` |
|
|
116
|
+
| checkpoint evidence | `head`, `changed`, `staged`, `unstaged`, `untracked`, `in_claim`, `out_of_claim` |
|
|
117
|
+
| repository evidence | `session_id`, `base_revision`, `head`, `clean`, `paths.stats`, `evidence_hash` |
|
|
118
|
+
| bounded diff | `from_revision`, `to_revision`, `paths`, `stats`, `patch`, `evidence_hash` |
|
|
119
|
+
| commit/push | `commit_sha`, `remote`, `branch`, `target`, `relation` |
|
|
120
|
+
| reconciliation/cleanup | `clean`, `issues`, `candidates`, `cleaned`, `blocked`, `recovery_hints` |
|
|
119
121
|
|
|
120
122
|
Git subprocesses are bounded at 10 seconds and 64 KiB of output; checkpoint
|
|
121
123
|
evidence is bounded to 4,096 paths. `GIT_SPAWN_FAILED`, `GIT_TIMEOUT`,
|
|
@@ -162,10 +164,12 @@ machine identities; labels and branch names are separate display metadata.
|
|
|
162
164
|
git nawabari session create --branch feature/example --worktree ../example-worktree --json
|
|
163
165
|
git nawabari session id --json
|
|
164
166
|
git nawabari session show --json
|
|
167
|
+
git nawabari session show <session-id> --json
|
|
165
168
|
git nawabari session list --json
|
|
166
169
|
git nawabari status --json
|
|
167
170
|
git nawabari guard --json
|
|
168
171
|
git nawabari session close --json
|
|
172
|
+
git nawabari session discard --session <session-id> --json
|
|
169
173
|
git nawabari gc --dry-run --json
|
|
170
174
|
git nawabari doctor --json
|
|
171
175
|
```
|
|
@@ -185,7 +189,14 @@ the resolved parent of that session's worktree.
|
|
|
185
189
|
under the repository-scoped mutation lock. The default/integration worktree
|
|
186
190
|
and its protected branch cannot be session resources. `session id` and the
|
|
187
191
|
other current-session commands resolve ownership from the current worktree;
|
|
188
|
-
callers do not need to repeat the session ID.
|
|
192
|
+
callers do not need to repeat the session ID for current-owner operations.
|
|
193
|
+
|
|
194
|
+
Session-scoped commands use one target grammar: the canonical `--session <id>`
|
|
195
|
+
option is accepted everywhere, and `show`, `inspect`, `claim`, `claims`,
|
|
196
|
+
`release`, `update`, `close`, and `discard` also accept one positional
|
|
197
|
+
`<session-id>` immediately after the subcommand. Supplying both forms is
|
|
198
|
+
rejected as ambiguous. `session discard` always requires one explicit target
|
|
199
|
+
and never infers the current worktree owner.
|
|
189
200
|
|
|
190
201
|
Close is conservative. Dirty worktrees, ambiguous ownership, mismatched Git
|
|
191
202
|
state, and commits not proven reachable from the integration branch block
|
|
@@ -198,6 +209,17 @@ every candidate that is not safe. Cleanup revalidates the physical worktree,
|
|
|
198
209
|
branch, and `HEAD` observations immediately before each destructive Git
|
|
199
210
|
operation.
|
|
200
211
|
|
|
212
|
+
`session discard` is the sole explicit destructive abandonment path. It
|
|
213
|
+
revalidates repository identity, the selected session, exact worktree/branch
|
|
214
|
+
ownership, and session/worktree `HEAD` immediately before each Git mutation.
|
|
215
|
+
It may destroy the selected session's unintegrated commits and uncommitted
|
|
216
|
+
worktree contents, removes only that session's worktree/branch, releases only
|
|
217
|
+
its claims, and records `terminal_operation: "discard"` plus the pre-discard
|
|
218
|
+
`HEAD`. It never acts as an implicit fallback for `close`, `gc`, `doctor`, or
|
|
219
|
+
reconciliation; sibling sessions remain untouched. A partial failure leaves a
|
|
220
|
+
retryable closing record and a repeated discard converges or returns an
|
|
221
|
+
explicit terminal idempotent result.
|
|
222
|
+
|
|
201
223
|
Routine `session list` and `status` output excludes `closed` history and is
|
|
202
224
|
limited to 64 records. Use `--all` (or `--history`) for an explicit complete
|
|
203
225
|
history view; closed records remain persisted and are never silently deleted
|
|
@@ -363,8 +385,14 @@ differs.
|
|
|
363
385
|
could not prove the branch safe and a `--integrated-revision` proof might
|
|
364
386
|
resolve it (e.g. after a squash/rebase merge), versus `blocked` when
|
|
365
387
|
supplied evidence failed to prove equivalence, versus `ambiguous` when Git
|
|
366
|
-
observation itself was inconclusive.
|
|
367
|
-
|
|
388
|
+
observation itself was inconclusive. For supplied evidence, bounded
|
|
389
|
+
`proofFailure` details distinguish an unauthoritative revision from a
|
|
390
|
+
tree-equivalence failure. The bounded evidence also includes
|
|
391
|
+
`currentSessionHead`, `suppliedIntegratedRevision`, `resolvedIntegrationSha`,
|
|
392
|
+
`lineageProof`/`authorityProof`, and `contentProof`. `safe_actions` includes
|
|
393
|
+
`discard-session` only as an explicit user choice; it does not authorize an
|
|
394
|
+
implicit cleanup fallback. Both surfaces reuse one authority, so a raw close
|
|
395
|
+
rejection and `session inspect` never drift apart.
|
|
368
396
|
|
|
369
397
|
None of the above weakens fail-closed behavior, changes an error code's
|
|
370
398
|
meaning, or performs any mutation while producing the rejection.
|
package/dist/cli.js
CHANGED
|
@@ -48,13 +48,16 @@ const HELP_COMMANDS = [
|
|
|
48
48
|
{
|
|
49
49
|
name: "session show",
|
|
50
50
|
summary: "Show the current or selected session",
|
|
51
|
-
usage: `${CLI_NAME} session show [
|
|
51
|
+
usage: `${CLI_NAME} session show [<session-id>|--session <id>]`,
|
|
52
52
|
options: [option("--session", "Select a session instead of the current worktree owner", { value: "<id>" })],
|
|
53
|
+
notes: [
|
|
54
|
+
"Target grammar is consistent: an optional first positional <session-id> is an alias for --session <id>; do not supply both.",
|
|
55
|
+
],
|
|
53
56
|
},
|
|
54
57
|
{
|
|
55
58
|
name: "session inspect",
|
|
56
59
|
summary: "Report side-effect-free close/cleanup readiness for a session",
|
|
57
|
-
usage: `${CLI_NAME} session inspect [
|
|
60
|
+
usage: `${CLI_NAME} session inspect [<session-id>|--session <id>] [--integrated-revision <rev>]`,
|
|
58
61
|
options: [
|
|
59
62
|
option("--session", "Select a session instead of the current worktree owner", { value: "<id>" }),
|
|
60
63
|
option("--integrated-revision", "Externally evidenced revision to test for non-ancestry (squash/rebase) integration; independently re-verified via exact Git tree-object equivalence, never trusted blindly", { value: "<rev>" }),
|
|
@@ -63,6 +66,7 @@ const HELP_COMMANDS = [
|
|
|
63
66
|
"Read-only: never mutates session, claim, Git, worktree, branch, or registry state. Repeated calls are idempotent.",
|
|
64
67
|
"Derived from the same authoritative close/cleanup Git evidence as session close; does not duplicate or diverge from that logic.",
|
|
65
68
|
"Nawabari never queries GitHub or any remote provider; --integrated-revision only names a local revision for Nawabari to independently verify.",
|
|
69
|
+
"Target grammar: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
|
|
66
70
|
],
|
|
67
71
|
},
|
|
68
72
|
{
|
|
@@ -88,18 +92,21 @@ const HELP_COMMANDS = [
|
|
|
88
92
|
{
|
|
89
93
|
name: "session claim",
|
|
90
94
|
summary: "Add a canonical resource claim",
|
|
91
|
-
usage: `${CLI_NAME} session claim --resource <path-or-glob> --mode <read|write|exclusive-write
|
|
95
|
+
usage: `${CLI_NAME} session claim [<session-id>] --resource <path-or-glob> --mode <read|write|exclusive-write>`,
|
|
92
96
|
options: [
|
|
93
97
|
option("--resource", "Repository-relative resource", { value: "<path-or-glob>", required: true }),
|
|
94
98
|
option("--mode", "Granted claim mode", { value: "<read|write|exclusive-write>", required: true }),
|
|
95
99
|
option("--session", "Target active session; omitted resolves the current owner", { value: "<id>" }),
|
|
96
100
|
option("--repository", "Expected repository identity", { value: "<id>" }),
|
|
97
101
|
],
|
|
102
|
+
notes: [
|
|
103
|
+
"Target grammar: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
|
|
104
|
+
],
|
|
98
105
|
},
|
|
99
106
|
{
|
|
100
107
|
name: "session update",
|
|
101
108
|
summary: "Atomically replace a session's complete resource claim set",
|
|
102
|
-
usage: `${CLI_NAME} session update --resource <path-or-glob> --mode <read|write|exclusive-write> [--resource <path-or-glob> --mode <read|write|exclusive-write> ...]
|
|
109
|
+
usage: `${CLI_NAME} session update [<session-id>] --resource <path-or-glob> --mode <read|write|exclusive-write> [--resource <path-or-glob> --mode <read|write|exclusive-write> ...]`,
|
|
103
110
|
options: [
|
|
104
111
|
option("--resource", "Repository-relative resource; repeatable, each paired with the --mode immediately after it", {
|
|
105
112
|
value: "<path-or-glob>",
|
|
@@ -116,33 +123,43 @@ const HELP_COMMANDS = [
|
|
|
116
123
|
"The desired claim set fully replaces the session's current claims in one updateClaims() transaction; " +
|
|
117
124
|
"on any invalid or conflicting claim the prior set is left unchanged.",
|
|
118
125
|
"Each --resource must be immediately followed by its own --mode; pairing is positional adjacency, not flag order.",
|
|
126
|
+
"Target grammar: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
|
|
119
127
|
],
|
|
120
128
|
},
|
|
121
129
|
{
|
|
122
130
|
name: "session claims",
|
|
123
131
|
summary: "List canonical resource claims",
|
|
124
|
-
usage: `${CLI_NAME} session claims [
|
|
132
|
+
usage: `${CLI_NAME} session claims [<session-id>|--session <id>]`,
|
|
125
133
|
options: [option("--session", "Select a session; omitted lists all claims", { value: "<id>" })],
|
|
134
|
+
notes: [
|
|
135
|
+
"Target grammar: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
|
|
136
|
+
],
|
|
126
137
|
},
|
|
127
138
|
{
|
|
128
139
|
name: "session release",
|
|
129
140
|
summary: "Release resource claims",
|
|
130
|
-
usage: `${CLI_NAME} session release [
|
|
141
|
+
usage: `${CLI_NAME} session release [<session-id>|--session <id>] [--claim-id <id>]`,
|
|
131
142
|
options: [
|
|
132
143
|
option("--session", "Target session; omitted resolves the current owner", { value: "<id>" }),
|
|
133
144
|
option("--claim-id", "Release only one claim; omitted releases all owned claims", { value: "<id>" }),
|
|
134
145
|
],
|
|
146
|
+
notes: [
|
|
147
|
+
"Target grammar: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
|
|
148
|
+
],
|
|
135
149
|
},
|
|
136
150
|
{
|
|
137
151
|
name: "resource claim",
|
|
138
152
|
summary: "Add a canonical resource claim (alias)",
|
|
139
|
-
usage: `${CLI_NAME} resource claim --resource <path-or-glob> --mode <read|write|exclusive-write>`,
|
|
153
|
+
usage: `${CLI_NAME} resource claim [<session-id>|--session <id>] --resource <path-or-glob> --mode <read|write|exclusive-write>`,
|
|
140
154
|
options: [],
|
|
155
|
+
notes: [
|
|
156
|
+
"Target grammar matches session claim: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
|
|
157
|
+
],
|
|
141
158
|
},
|
|
142
159
|
{
|
|
143
160
|
name: "resource update",
|
|
144
161
|
summary: "Atomically replace a session's complete resource claim set (alias)",
|
|
145
|
-
usage: `${CLI_NAME} resource update --resource <path-or-glob> --mode <read|write|exclusive-write> [--resource <path-or-glob> --mode <read|write|exclusive-write> ...]
|
|
162
|
+
usage: `${CLI_NAME} resource update [<session-id>|--session <id>] --resource <path-or-glob> --mode <read|write|exclusive-write> [--resource <path-or-glob> --mode <read|write|exclusive-write> ...]`,
|
|
146
163
|
options: [
|
|
147
164
|
option("--resource", "Repository-relative resource; repeatable, each paired with the --mode immediately after it", {
|
|
148
165
|
value: "<path-or-glob>",
|
|
@@ -159,27 +176,34 @@ const HELP_COMMANDS = [
|
|
|
159
176
|
"The desired claim set fully replaces the session's current claims in one updateClaims() transaction; " +
|
|
160
177
|
"on any invalid or conflicting claim the prior set is left unchanged.",
|
|
161
178
|
"Each --resource must be immediately followed by its own --mode; pairing is positional adjacency, not flag order.",
|
|
179
|
+
"Target grammar matches session update: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
|
|
162
180
|
],
|
|
163
181
|
},
|
|
164
182
|
{
|
|
165
183
|
name: "resource list",
|
|
166
184
|
summary: "List canonical resource claims (alias)",
|
|
167
|
-
usage: `${CLI_NAME} resource list [
|
|
185
|
+
usage: `${CLI_NAME} resource list [<session-id>|--session <id>]`,
|
|
168
186
|
options: [option("--session", "Select a session; omitted lists all claims", { value: "<id>" })],
|
|
187
|
+
notes: [
|
|
188
|
+
"Target grammar: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
|
|
189
|
+
],
|
|
169
190
|
},
|
|
170
191
|
{
|
|
171
192
|
name: "resource release",
|
|
172
193
|
summary: "Release resource claims (alias)",
|
|
173
|
-
usage: `${CLI_NAME} resource release [
|
|
194
|
+
usage: `${CLI_NAME} resource release [<session-id>|--session <id>] [--claim-id <id>]`,
|
|
174
195
|
options: [
|
|
175
196
|
option("--session", "Target session; omitted resolves the current owner", { value: "<id>" }),
|
|
176
197
|
option("--claim-id", "Release only one claim; omitted releases all owned claims", { value: "<id>" }),
|
|
177
198
|
],
|
|
199
|
+
notes: [
|
|
200
|
+
"Target grammar: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
|
|
201
|
+
],
|
|
178
202
|
},
|
|
179
203
|
{
|
|
180
204
|
name: "session close",
|
|
181
205
|
summary: "Close the current or selected session",
|
|
182
|
-
usage: `${CLI_NAME} session close [
|
|
206
|
+
usage: `${CLI_NAME} session close [<session-id>|--session <id>] [--integrated-revision <rev>]`,
|
|
183
207
|
options: [
|
|
184
208
|
option("--session", "Select a session instead of the current worktree owner", { value: "<id>" }),
|
|
185
209
|
option("--integrated-revision", "Externally evidenced revision proving non-ancestry (squash/rebase) integration; independently re-verified via exact Git tree-object equivalence, never trusted blindly", { value: "<rev>" }),
|
|
@@ -187,6 +211,24 @@ const HELP_COMMANDS = [
|
|
|
187
211
|
notes: [
|
|
188
212
|
"Ordinary ancestry-based close remains the cheap/default path and requires no flags.",
|
|
189
213
|
"Nawabari never queries GitHub or any remote provider; --integrated-revision only names a local revision for Nawabari to independently verify.",
|
|
214
|
+
"Target grammar: optional first positional <session-id> is an alias for --session <id>; do not supply both.",
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: "session discard",
|
|
219
|
+
summary: "Explicitly discard one selected session and its owned resources",
|
|
220
|
+
usage: `${CLI_NAME} session discard <session-id>|--session <id>`,
|
|
221
|
+
options: [
|
|
222
|
+
option("--session", "Required explicit target; the current session is never inferred", {
|
|
223
|
+
value: "<id>",
|
|
224
|
+
required: true,
|
|
225
|
+
}),
|
|
226
|
+
],
|
|
227
|
+
notes: [
|
|
228
|
+
"Destructive and explicit: unintegrated commits and uncommitted work in the selected owned worktree may be destroyed.",
|
|
229
|
+
"Discard never changes close, gc, doctor, reconciliation, or integration-lineage proof behavior.",
|
|
230
|
+
"Use exactly one target form: positional <session-id> or --session <id>; do not supply both.",
|
|
231
|
+
"Machine JSON mode is non-interactive and deterministic.",
|
|
190
232
|
],
|
|
191
233
|
},
|
|
192
234
|
{
|
|
@@ -347,6 +389,13 @@ function helpPayload(spec) {
|
|
|
347
389
|
commit_options: optionsFor("commit"),
|
|
348
390
|
push_options: optionsFor("push"),
|
|
349
391
|
gc_options: optionsFor("gc"),
|
|
392
|
+
session_targeting: {
|
|
393
|
+
canonical: "--session <id>",
|
|
394
|
+
positional_alias: "<session-id> as the first argument after a session-scoped subcommand",
|
|
395
|
+
commands: ["show", "inspect", "claim", "claims", "release", "update", "close", "discard"],
|
|
396
|
+
ambiguity: "supplying both positional and --session is rejected",
|
|
397
|
+
discard_requires_explicit_target: true,
|
|
398
|
+
},
|
|
350
399
|
};
|
|
351
400
|
}
|
|
352
401
|
const options = spec.options.map((candidate) => ({
|
|
@@ -579,6 +628,34 @@ function parseOptions(arguments_, allowed) {
|
|
|
579
628
|
function noOptions(arguments_) {
|
|
580
629
|
return parseOptions(arguments_, new Set());
|
|
581
630
|
}
|
|
631
|
+
/** One unambiguous positional alias shared by every session-targeted command. */
|
|
632
|
+
function splitPositionalSessionTarget(arguments_) {
|
|
633
|
+
const first = arguments_[0];
|
|
634
|
+
if (first === undefined || first.startsWith("-")) {
|
|
635
|
+
return { ok: true, value: { sessionId: null, arguments: arguments_ } };
|
|
636
|
+
}
|
|
637
|
+
return { ok: true, value: { sessionId: first, arguments: arguments_.slice(1) } };
|
|
638
|
+
}
|
|
639
|
+
function parseTargetedOptions(arguments_, allowed, required = false) {
|
|
640
|
+
const positional = splitPositionalSessionTarget(arguments_);
|
|
641
|
+
if (!positional.ok)
|
|
642
|
+
return positional;
|
|
643
|
+
const parsed = parseOptions(positional.value.arguments, allowed);
|
|
644
|
+
if (!parsed.ok)
|
|
645
|
+
return parsed;
|
|
646
|
+
if (positional.value.sessionId !== null && parsed.value.session_id !== null) {
|
|
647
|
+
return failure(usageError("INVALID_ARGUMENT", "Specify a session target either positionally or with --session, not both.", {
|
|
648
|
+
option: "--session",
|
|
649
|
+
}));
|
|
650
|
+
}
|
|
651
|
+
parsed.value.session_id = positional.value.sessionId ?? parsed.value.session_id;
|
|
652
|
+
if (required && parsed.value.session_id === null) {
|
|
653
|
+
return failure(usageError("MISSING_ARGUMENT", "An explicit session target is required; use <session-id> or --session <id>.", {
|
|
654
|
+
option: "--session",
|
|
655
|
+
}));
|
|
656
|
+
}
|
|
657
|
+
return parsed;
|
|
658
|
+
}
|
|
582
659
|
/**
|
|
583
660
|
* `update` accepts a complete desired claim set as repeated
|
|
584
661
|
* `--resource <path> --mode <mode>` pairs. Pairing is by strict local
|
|
@@ -588,26 +665,35 @@ function noOptions(arguments_) {
|
|
|
588
665
|
* with the wrong mode.
|
|
589
666
|
*/
|
|
590
667
|
function parseClaimReplacementPairs(arguments_) {
|
|
668
|
+
const positional = splitPositionalSessionTarget(arguments_);
|
|
669
|
+
if (!positional.ok)
|
|
670
|
+
return positional;
|
|
591
671
|
let sessionId = null;
|
|
592
672
|
let repository = null;
|
|
593
673
|
const pairs = [];
|
|
594
674
|
let pendingResource = null;
|
|
595
|
-
|
|
596
|
-
|
|
675
|
+
sessionId = positional.value.sessionId;
|
|
676
|
+
const targetArguments = positional.value.arguments;
|
|
677
|
+
for (let index = 0; index < targetArguments.length; index += 1) {
|
|
678
|
+
const { name, inlineValue } = optionParts(targetArguments[index]);
|
|
597
679
|
if (name !== "--session" && name !== "--repository" && name !== "--resource" && name !== "--mode") {
|
|
598
680
|
return failure(usageError("INVALID_ARGUMENT", `Unknown option: ${name}.`, { option: name }));
|
|
599
681
|
}
|
|
600
682
|
if (pendingResource !== null && name !== "--mode") {
|
|
601
683
|
return failure(usageError("INVALID_ARGUMENT", `${name} cannot appear between --resource and its --mode; only --mode is permitted immediately after --resource.`, { option: name }));
|
|
602
684
|
}
|
|
603
|
-
const value = inlineValue ??
|
|
685
|
+
const value = inlineValue ?? targetArguments[index + 1];
|
|
604
686
|
if (value === undefined || value === "" || (inlineValue === null && value.startsWith("-"))) {
|
|
605
687
|
return failure(usageError("MISSING_ARGUMENT", `${name} requires a value.`, { option: name }));
|
|
606
688
|
}
|
|
607
689
|
if (inlineValue === null)
|
|
608
690
|
index += 1;
|
|
609
|
-
if (name === "--session")
|
|
691
|
+
if (name === "--session") {
|
|
692
|
+
if (sessionId !== null) {
|
|
693
|
+
return failure(usageError("INVALID_ARGUMENT", "Specify a session target either positionally or with --session, not both."));
|
|
694
|
+
}
|
|
610
695
|
sessionId = value;
|
|
696
|
+
}
|
|
611
697
|
else if (name === "--repository")
|
|
612
698
|
repository = value;
|
|
613
699
|
else if (name === "--resource")
|
|
@@ -745,7 +831,7 @@ async function executeCommand(commandArguments, dependencies) {
|
|
|
745
831
|
return result.ok ? { ok: true, value: result.value } : result;
|
|
746
832
|
}
|
|
747
833
|
if (subcommand === "claims") {
|
|
748
|
-
const parsed =
|
|
834
|
+
const parsed = parseTargetedOptions(rest, new Set(["--session"]));
|
|
749
835
|
if (!parsed.ok)
|
|
750
836
|
return parsed;
|
|
751
837
|
if (dependencies.backend.listClaims === undefined)
|
|
@@ -754,7 +840,7 @@ async function executeCommand(commandArguments, dependencies) {
|
|
|
754
840
|
return result.ok ? { ok: true, value: result.value } : result;
|
|
755
841
|
}
|
|
756
842
|
if (subcommand === "release") {
|
|
757
|
-
const parsed =
|
|
843
|
+
const parsed = parseTargetedOptions(rest, new Set(["--session", "--claim-id"]));
|
|
758
844
|
if (!parsed.ok)
|
|
759
845
|
return parsed;
|
|
760
846
|
if (dependencies.backend.releaseClaims === undefined)
|
|
@@ -805,7 +891,7 @@ async function executeCommand(commandArguments, dependencies) {
|
|
|
805
891
|
return result.ok ? { ok: true, value: result.value } : result;
|
|
806
892
|
}
|
|
807
893
|
if (subcommand === "inspect") {
|
|
808
|
-
const parsed =
|
|
894
|
+
const parsed = parseTargetedOptions(rest, new Set(["--session", "--integrated-revision"]));
|
|
809
895
|
if (!parsed.ok)
|
|
810
896
|
return parsed;
|
|
811
897
|
if (dependencies.backend.sessionDiagnostic === undefined)
|
|
@@ -817,6 +903,15 @@ async function executeCommand(commandArguments, dependencies) {
|
|
|
817
903
|
const result = await dependencies.backend.sessionDiagnostic(context, options);
|
|
818
904
|
return result.ok ? { ok: true, value: result.value } : result;
|
|
819
905
|
}
|
|
906
|
+
if (subcommand === "discard") {
|
|
907
|
+
const parsed = parseTargetedOptions(rest, new Set(["--session"]), true);
|
|
908
|
+
if (!parsed.ok)
|
|
909
|
+
return parsed;
|
|
910
|
+
if (dependencies.backend.discardSession === undefined)
|
|
911
|
+
return sessionDiscardCapabilityUnavailable();
|
|
912
|
+
const result = await dependencies.backend.discardSession(context, parsed.value.session_id);
|
|
913
|
+
return result.ok ? { ok: true, value: result.value } : result;
|
|
914
|
+
}
|
|
820
915
|
if (subcommand === "id" || subcommand === "show" || subcommand === "close") {
|
|
821
916
|
if (subcommand === "id") {
|
|
822
917
|
const parsed = noOptions(rest);
|
|
@@ -827,7 +922,7 @@ async function executeCommand(commandArguments, dependencies) {
|
|
|
827
922
|
return selected;
|
|
828
923
|
return { ok: true, value: { session_id: selected.value.session_id } };
|
|
829
924
|
}
|
|
830
|
-
const parsed =
|
|
925
|
+
const parsed = parseTargetedOptions(rest, new Set(subcommand === "close" ? ["--session", "--integrated-revision"] : ["--session"]));
|
|
831
926
|
if (!parsed.ok)
|
|
832
927
|
return parsed;
|
|
833
928
|
if (subcommand === "close") {
|
|
@@ -885,7 +980,7 @@ async function executeCommand(commandArguments, dependencies) {
|
|
|
885
980
|
return result.ok ? { ok: true, value: result.value } : result;
|
|
886
981
|
}
|
|
887
982
|
if (resourceSubcommand === "list" || resourceSubcommand === "claims") {
|
|
888
|
-
const parsed =
|
|
983
|
+
const parsed = parseTargetedOptions(rest, new Set(["--session"]));
|
|
889
984
|
if (!parsed.ok)
|
|
890
985
|
return parsed;
|
|
891
986
|
if (dependencies.backend.listClaims === undefined)
|
|
@@ -894,7 +989,7 @@ async function executeCommand(commandArguments, dependencies) {
|
|
|
894
989
|
return result.ok ? { ok: true, value: result.value } : result;
|
|
895
990
|
}
|
|
896
991
|
if (resourceSubcommand === "release") {
|
|
897
|
-
const parsed =
|
|
992
|
+
const parsed = parseTargetedOptions(rest, new Set(["--session", "--claim-id"]));
|
|
898
993
|
if (!parsed.ok)
|
|
899
994
|
return parsed;
|
|
900
995
|
if (dependencies.backend.releaseClaims === undefined)
|
|
@@ -1151,6 +1246,11 @@ function sessionDiagnosticCapabilityUnavailable() {
|
|
|
1151
1246
|
operation: "session.inspect",
|
|
1152
1247
|
}));
|
|
1153
1248
|
}
|
|
1249
|
+
function sessionDiscardCapabilityUnavailable() {
|
|
1250
|
+
return failure(new DomainError("BACKEND_UNAVAILABLE", "Session discard capability is not available.", {
|
|
1251
|
+
operation: "session.discard",
|
|
1252
|
+
}));
|
|
1253
|
+
}
|
|
1154
1254
|
function repositoryEvidenceCapabilityUnavailable(operation) {
|
|
1155
1255
|
return failure(new DomainError("BACKEND_UNAVAILABLE", "Repository evidence capability is not available.", { operation }));
|
|
1156
1256
|
}
|