systemlink-cli 1.6.2__tar.gz → 1.6.4__tar.gz
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.
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/PKG-INFO +1 -1
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/pyproject.toml +1 -1
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/_version.py +1 -1
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/skills/systemlink-webapp/SKILL.md +22 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/skills/systemlink-webapp/references/nimble-angular.md +14 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/user_click.py +99 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/utils.py +1 -1
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/LICENSE +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/dff-editor/editor.js +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/dff-editor/index.html +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/__init__.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/__main__.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/asset_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/cli_formatters.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/cli_utils.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/comment_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/completion_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/config.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/config_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/dff_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/dff_decorators.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/example_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/example_loader.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/example_provisioner.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/README.md +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/_schema/schema-v1.0.json +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/demo-complete-workflow/README.md +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/demo-complete-workflow/config.yaml +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/demo-test-plans/README.md +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/demo-test-plans/config.yaml +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/exercise-5-1-parametric-insights/README.md +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/exercise-5-1-parametric-insights/config.yaml +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/exercise-7-1-test-plans/README.md +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/exercise-7-1-test-plans/config.yaml +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/spec-compliance-notebooks/README.md +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/spec-compliance-notebooks/config.yaml +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/feed_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/file_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/function_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/function_templates.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/main.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/mcp_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/mcp_server.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/notebook_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/platform.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/policy_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/policy_utils.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/profiles.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/response_handlers.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/rich_output.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/routine_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/skill_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/skills/slcli/SKILL.md +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/skills/slcli/references/analysis-recipes.md +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/skills/slcli/references/filtering.md +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/skills/systemlink-webapp/references/deployment.md +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/skills/systemlink-webapp/references/layout-patterns.md +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/skills/systemlink-webapp/references/systemlink-services.md +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/ssl_trust.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/system_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/table_utils.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/tag_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/templates_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/testmonitor_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/universal_handlers.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/web_editor.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/webapp_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/workflow_preview.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/workflows_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/workitem_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/workspace_click.py +0 -0
- {systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/workspace_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "systemlink-cli"
|
|
3
|
-
version = "1.6.
|
|
3
|
+
version = "1.6.4"
|
|
4
4
|
description = "SystemLink Integrator CLI - cross-platform CLI for SystemLink workflows and templates."
|
|
5
5
|
authors = ["Fred Visser <fred.visser@emerson.com>"]
|
|
6
6
|
packages = [{ include = "slcli" }]
|
|
@@ -161,6 +161,9 @@ import { APP_BASE_HREF } from '@angular/common';
|
|
|
161
161
|
// Most Nimble component modules are exported from the main `@ni/nimble-angular` barrel.
|
|
162
162
|
// Icon modules (e.g. NimbleIconMagnifyingGlassModule) are ONLY in the main barrel —
|
|
163
163
|
// sub-paths like `@ni/nimble-angular/icons/magnifying-glass` do NOT exist.
|
|
164
|
+
// Do NOT add `@ni/nimble-components` or register raw custom elements just to make
|
|
165
|
+
// Angular templates compile. If a Nimble element is unknown, import the missing
|
|
166
|
+
// Angular module from `@ni/nimble-angular` instead.
|
|
164
167
|
import {
|
|
165
168
|
NimbleThemeProviderModule,
|
|
166
169
|
NimbleButtonModule,
|
|
@@ -214,8 +217,25 @@ import { MyFeatureComponent } from './my-feature/my-feature.component';
|
|
|
214
217
|
export class AppModule {}
|
|
215
218
|
```
|
|
216
219
|
|
|
220
|
+
When using `@ni/nimble-angular`, prefer the Angular wrappers end-to-end:
|
|
221
|
+
|
|
222
|
+
- Do **not** add `@ni/nimble-components` as a direct dependency just to register icons or other raw custom elements.
|
|
223
|
+
- Do **not** use `CUSTOM_ELEMENTS_SCHEMA` as a workaround for unknown Nimble elements. In this codebase that is usually a sign that the corresponding Nimble Angular module import is missing, and `CUSTOM_ELEMENTS_SCHEMA` suppresses valuable template checking.
|
|
224
|
+
- If Angular reports an unknown Nimble tag, fix the module imports first.
|
|
225
|
+
|
|
217
226
|
For Nimble form controls (`nimble-text-field`, `nimble-select`, etc.), bind with Angular forms APIs (`[(ngModel)]`, `[formControl]`, or `formControlName`) and use `(ngModelChange)` for value-change reactions. Avoid native control bindings like `[value]`, `(input)`, or `(change)` on Nimble elements.
|
|
218
227
|
|
|
228
|
+
For control labels, prefer Nimble's built-in label pattern by slotting text content inside the control instead of pairing the control with a separate HTML `<label>` element for the primary label:
|
|
229
|
+
|
|
230
|
+
```html
|
|
231
|
+
<nimble-select [(ngModel)]="selectedWorkspace">
|
|
232
|
+
Workspace
|
|
233
|
+
<nimble-list-option *ngFor="let ws of workspaces" [value]="ws.id">
|
|
234
|
+
{{ ws.name }}
|
|
235
|
+
</nimble-list-option>
|
|
236
|
+
</nimble-select>
|
|
237
|
+
```
|
|
238
|
+
|
|
219
239
|
**Critical:** Provide `APP_BASE_HREF` via DI and **remove the `<base href="/">` tag from `index.html`**. SystemLink enforces a `base-uri 'self'` CSP directive; the `<base>` element violates it.
|
|
220
240
|
|
|
221
241
|
---
|
|
@@ -737,6 +757,8 @@ Save the returned webapp ID — you'll need it for every subsequent redeploy.
|
|
|
737
757
|
| `InputFieldValidationError` on API call | SDK-generated request body has wrong shape | Inspect raw API; the generated type may add or omit a `request: {}` wrapper. Use direct `fetch` with manually constructed body |
|
|
738
758
|
| nimble-dialog does not open | `*ngIf` destroys element before `ViewChild` can resolve | Remove `*ngIf` from the dialog element; use `@ViewChild` + `ElementRef` and call `nativeElement.show()` / `nativeElement.close()` |
|
|
739
759
|
| Icon module import fails | Icon sub-path `@ni/nimble-angular/icons/...` does not exist | Import icon modules from the main `@ni/nimble-angular` barrel only |
|
|
760
|
+
| Angular says a Nimble tag is unknown | Missing `@ni/nimble-angular` module import | Import the missing wrapper module instead of adding `CUSTOM_ELEMENTS_SCHEMA` or registering raw `@ni/nimble-components` elements |
|
|
761
|
+
| Nimble form control label looks detached or duplicated | Used a separate HTML `<label>` for the primary control label | Slot the label text inside `nimble-text-field`, `nimble-select`, and similar controls |
|
|
740
762
|
| Table rows empty despite correct response | `projection` flattens nested objects | Remove `projection` from query body |
|
|
741
763
|
| `TableRecord` type error | Row type missing index signature | Add `[key: string]: FieldValue \| undefined` |
|
|
742
764
|
| Button appearance invalid | Wrong value for `appearance` attr | Use `appearance="block" appearance-variant="accent"` |
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Nimble Angular — Template & Usage Reference
|
|
2
2
|
|
|
3
|
+
## Wrapper-first rule
|
|
4
|
+
|
|
5
|
+
When building Angular apps with Nimble, use `@ni/nimble-angular` wrapper modules as the default integration path.
|
|
6
|
+
|
|
7
|
+
- Import the needed Angular module for each Nimble control instead of registering raw custom elements from `@ni/nimble-components`.
|
|
8
|
+
- Do not add `CUSTOM_ELEMENTS_SCHEMA` just to silence unknown Nimble elements in templates. That usually hides a missing module import and weakens Angular's template validation.
|
|
9
|
+
- If a Nimble icon or control is unknown, first look for the matching module in `@ni/nimble-angular` and import it.
|
|
10
|
+
|
|
3
11
|
## nimble-theme-provider
|
|
4
12
|
|
|
5
13
|
Wrap your entire app. Always place at the root component level.
|
|
@@ -176,6 +184,8 @@ import { NimbleTextFieldModule } from "@ni/nimble-angular";
|
|
|
176
184
|
</nimble-text-field>
|
|
177
185
|
```
|
|
178
186
|
|
|
187
|
+
Use the text inside the element as the control's primary label. Do not add a separate HTML `<label>` element for the basic Nimble control label unless you have a specific accessibility or layout need that cannot be expressed with Nimble's built-in labeling pattern.
|
|
188
|
+
|
|
179
189
|
Import icon modules from the **main `@ni/nimble-angular` barrel** — icon-specific sub-paths do not exist:
|
|
180
190
|
|
|
181
191
|
```typescript
|
|
@@ -195,6 +205,7 @@ import { NimbleSelectModule, NimbleListOptionModule } from "@ni/nimble-angular";
|
|
|
195
205
|
```html
|
|
196
206
|
<!-- Basic -->
|
|
197
207
|
<nimble-select [(ngModel)]="selectedType" (ngModelChange)="onTypeChange()">
|
|
208
|
+
Type
|
|
198
209
|
<nimble-list-option value="">All types</nimble-list-option>
|
|
199
210
|
<nimble-list-option value="DOUBLE">Double</nimble-list-option>
|
|
200
211
|
<nimble-list-option value="STRING">String</nimble-list-option>
|
|
@@ -203,12 +214,15 @@ import { NimbleSelectModule, NimbleListOptionModule } from "@ni/nimble-angular";
|
|
|
203
214
|
|
|
204
215
|
<!-- With built-in filter (useful for long lists) -->
|
|
205
216
|
<nimble-select filter-mode="standard" [(ngModel)]="selectedWorkspace">
|
|
217
|
+
Workspace
|
|
206
218
|
<nimble-list-option *ngFor="let ws of workspaces" [value]="ws.id"
|
|
207
219
|
>{{ ws.name }}</nimble-list-option
|
|
208
220
|
>
|
|
209
221
|
</nimble-select>
|
|
210
222
|
```
|
|
211
223
|
|
|
224
|
+
Use the slotted text content as the select label instead of pairing the control with a separate HTML `<label>` for the primary label.
|
|
225
|
+
|
|
212
226
|
> Use `filter-mode="standard"` to add a built-in text filter to the dropdown — no custom search logic needed for long option lists.
|
|
213
227
|
|
|
214
228
|
---
|
|
@@ -29,6 +29,103 @@ from .workspace_utils import get_workspace_display_name, resolve_workspace_id
|
|
|
29
29
|
|
|
30
30
|
USER_QUERY_PAGE_SIZE = 100
|
|
31
31
|
USER_JSON_DEFAULT_TAKE = 1000
|
|
32
|
+
AUTH_WILDCARD_VALUES = {"*", "*/*", "*:*"}
|
|
33
|
+
AUTH_MUTATING_VERBS = {"*", "create", "write", "update", "manage", "admin", "delete"}
|
|
34
|
+
AUTH_ADMIN_RESOURCE_KEYWORDS = {"policy", "policies", "role", "roles"}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _has_global_auth_scope(value: Any) -> bool:
|
|
38
|
+
"""Return whether an auth scope value grants wildcard access."""
|
|
39
|
+
if isinstance(value, str):
|
|
40
|
+
return value.strip().lower() in AUTH_WILDCARD_VALUES
|
|
41
|
+
if isinstance(value, list):
|
|
42
|
+
return any(
|
|
43
|
+
isinstance(item, str) and item.strip().lower() in AUTH_WILDCARD_VALUES for item in value
|
|
44
|
+
)
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _action_grants_auth_management(action: str) -> bool:
|
|
49
|
+
"""Return whether an action implies auth policy or role management access."""
|
|
50
|
+
normalized = action.strip().lower()
|
|
51
|
+
if normalized in AUTH_WILDCARD_VALUES:
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
action_parts = [part.strip().lower() for part in normalized.split(":") if part.strip()]
|
|
55
|
+
if len(action_parts) < 2 or action_parts[0] not in {"niauth", "auth"}:
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
remainder = action_parts[1:]
|
|
59
|
+
if remainder == ["*"]:
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
if remainder[-1] not in AUTH_MUTATING_VERBS and "*" not in remainder:
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
if len(remainder) == 1:
|
|
66
|
+
return True
|
|
67
|
+
|
|
68
|
+
return any(part in AUTH_ADMIN_RESOURCE_KEYWORDS for part in remainder[:-1])
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _has_user_admin_access(auth_context: Dict[str, Any]) -> bool:
|
|
72
|
+
"""Return whether the current caller appears to have server-admin style access."""
|
|
73
|
+
for policy in auth_context.get("policies", []):
|
|
74
|
+
statements = policy.get("statements", [])
|
|
75
|
+
if not isinstance(statements, list):
|
|
76
|
+
continue
|
|
77
|
+
|
|
78
|
+
for statement in statements:
|
|
79
|
+
if not isinstance(statement, dict):
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
if not _has_global_auth_scope(statement.get("workspace")):
|
|
83
|
+
continue
|
|
84
|
+
if not _has_global_auth_scope(statement.get("resource")):
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
actions = statement.get("actions", [])
|
|
88
|
+
if not isinstance(actions, list):
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
for action in actions:
|
|
92
|
+
if isinstance(action, str) and _action_grants_auth_management(action):
|
|
93
|
+
return True
|
|
94
|
+
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _try_get_current_auth_context() -> Optional[Dict[str, Any]]:
|
|
99
|
+
"""Fetch effective auth data for the current caller when available."""
|
|
100
|
+
url = f"{get_base_url()}/niauth/v1/auth"
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
auth_response = make_api_request("GET", url, payload=None, handle_errors=False)
|
|
104
|
+
auth_context = auth_response.json()
|
|
105
|
+
return auth_context if isinstance(auth_context, dict) else None
|
|
106
|
+
except Exception as exc:
|
|
107
|
+
error_response = getattr(exc, "response", None)
|
|
108
|
+
if error_response is not None and error_response.status_code in {401, 403}:
|
|
109
|
+
handle_api_error(exc)
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _ensure_user_admin_access(operation: str) -> None:
|
|
114
|
+
"""Fail fast when the caller clearly lacks admin access for user mutations."""
|
|
115
|
+
auth_context = _try_get_current_auth_context()
|
|
116
|
+
if auth_context is None:
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
if _has_user_admin_access(auth_context):
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
click.echo(
|
|
123
|
+
f"✗ User and service account {operation} requires server admin permissions. "
|
|
124
|
+
"The active API key does not appear to have wildcard auth role or policy access "
|
|
125
|
+
"across all workspaces.",
|
|
126
|
+
err=True,
|
|
127
|
+
)
|
|
128
|
+
sys.exit(ExitCodes.PERMISSION_DENIED)
|
|
32
129
|
|
|
33
130
|
|
|
34
131
|
def _get_policy_details(policy_id: str) -> Optional[dict]:
|
|
@@ -851,6 +948,7 @@ def register_user_commands(cli: click.Group) -> None:
|
|
|
851
948
|
from .utils import check_readonly_mode
|
|
852
949
|
|
|
853
950
|
check_readonly_mode("create a user")
|
|
951
|
+
_ensure_user_admin_access("creation")
|
|
854
952
|
|
|
855
953
|
# If user_type wasn't specified via CLI, prompt for it first
|
|
856
954
|
if user_type is None:
|
|
@@ -1060,6 +1158,7 @@ def register_user_commands(cli: click.Group) -> None:
|
|
|
1060
1158
|
from .utils import check_readonly_mode
|
|
1061
1159
|
|
|
1062
1160
|
check_readonly_mode("update a user")
|
|
1161
|
+
_ensure_user_admin_access("updates")
|
|
1063
1162
|
|
|
1064
1163
|
# First, fetch the user to check if it's a service account
|
|
1065
1164
|
get_url = f"{get_base_url()}/niuser/v1/users/{user_id}"
|
|
@@ -75,7 +75,7 @@ def handle_api_error(exc: Exception) -> None:
|
|
|
75
75
|
if "not found" in error_msg:
|
|
76
76
|
click.echo(f"✗ Resource not found: {exc}", err=True)
|
|
77
77
|
sys.exit(ExitCodes.NOT_FOUND)
|
|
78
|
-
elif "permission" in error_msg or "unauthorized" in error_msg:
|
|
78
|
+
elif "permission" in error_msg or "unauthorized" in error_msg or "forbidden" in error_msg:
|
|
79
79
|
click.echo(f"✗ Permission denied: {exc}", err=True)
|
|
80
80
|
sys.exit(ExitCodes.PERMISSION_DENIED)
|
|
81
81
|
elif "network" in error_msg or "connection" in error_msg:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/demo-complete-workflow/README.md
RENAMED
|
File without changes
|
{systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/demo-complete-workflow/config.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/exercise-7-1-test-plans/README.md
RENAMED
|
File without changes
|
{systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/exercise-7-1-test-plans/config.yaml
RENAMED
|
File without changes
|
{systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/spec-compliance-notebooks/README.md
RENAMED
|
File without changes
|
{systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/examples/spec-compliance-notebooks/config.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{systemlink_cli-1.6.2 → systemlink_cli-1.6.4}/slcli/skills/slcli/references/analysis-recipes.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|