upscaler-cli 0.2.3.dev6696__tar.gz → 0.2.3.dev6699__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.
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/PKG-INFO +1 -1
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/pyproject.toml +1 -1
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/auth/oauth.py +5 -1
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/asset.py +211 -48
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/auth.py +11 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/context.py +1 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/entry.py +99 -88
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/get.py +30 -9
- upscaler_cli-0.2.3.dev6699/src/cli/helpers.py +334 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/hierarchy.py +3 -1
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/list_cmd.py +33 -7
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/main.py +102 -20
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/search.py +3 -1
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/todo.py +3 -1
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/client.py +11 -1
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/upscaler_cli.egg-info/PKG-INFO +1 -1
- upscaler_cli-0.2.3.dev6696/src/cli/helpers.py +0 -182
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/README.md +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/setup.cfg +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/__init__.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/auth/__init__.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/auth/encryption.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/auth/token_store.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/__init__.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/automation.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/completions.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/config_cmd.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/files.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/framework.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/cli/profile_cmd.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/config.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/errors.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/formatters/__init__.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/formatters/json_fmt.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/formatters/table.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/formatters/tree.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/profile.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/src/uploads.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/tests/test_client.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/tests/test_config.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/tests/test_profile.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/tests/test_uploads.py +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/upscaler_cli.egg-info/SOURCES.txt +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/upscaler_cli.egg-info/dependency_links.txt +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/upscaler_cli.egg-info/entry_points.txt +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/upscaler_cli.egg-info/requires.txt +0 -0
- {upscaler_cli-0.2.3.dev6696 → upscaler_cli-0.2.3.dev6699}/upscaler_cli.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "upscaler-cli"
|
|
7
|
-
version = "0.2.3.
|
|
7
|
+
version = "0.2.3.dev6699"
|
|
8
8
|
description = "Upscaler CLI - search, retrieve, and manage documents, records, and workflows"
|
|
9
9
|
requires-python = ">=3.10"
|
|
10
10
|
dependencies = [
|
|
@@ -477,7 +477,11 @@ class OAuthFlow:
|
|
|
477
477
|
expires_at=time.time() + data.get("expires_in", 86400),
|
|
478
478
|
client_id=token_data.client_id,
|
|
479
479
|
client_secret=token_data.client_secret,
|
|
480
|
-
|
|
480
|
+
# Read org from the refresh response (matching the login path) so a
|
|
481
|
+
# refreshed session reflects the token's real scope. Fall back to the
|
|
482
|
+
# existing value when the endpoint omits it, to avoid nulling a
|
|
483
|
+
# previously-valid org.
|
|
484
|
+
organization_id=data.get("organization_id") or token_data.organization_id,
|
|
481
485
|
token_endpoint=token_data.token_endpoint,
|
|
482
486
|
)
|
|
483
487
|
|
|
@@ -79,14 +79,29 @@ def asset_find(ctx, title, description, asset_type, limit, offset):
|
|
|
79
79
|
returned = metadata.get("returned_count", len(data))
|
|
80
80
|
if total > returned:
|
|
81
81
|
click.echo(f"Showing {returned} of {total} results (use --offset to paginate)\n")
|
|
82
|
-
click.echo(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
click.echo(
|
|
83
|
+
format_table(
|
|
84
|
+
data,
|
|
85
|
+
columns=["asset_id", "title", "asset_type", "description"],
|
|
86
|
+
)
|
|
87
|
+
)
|
|
86
88
|
|
|
87
89
|
|
|
88
90
|
@asset_group.command("create")
|
|
89
|
-
@click.option(
|
|
91
|
+
@click.option(
|
|
92
|
+
"--type",
|
|
93
|
+
"asset_type",
|
|
94
|
+
required=True,
|
|
95
|
+
type=click.Choice(
|
|
96
|
+
[
|
|
97
|
+
"document_definition",
|
|
98
|
+
"register_definition",
|
|
99
|
+
"record_definition",
|
|
100
|
+
"course_definition",
|
|
101
|
+
]
|
|
102
|
+
),
|
|
103
|
+
help="Asset type to create.",
|
|
104
|
+
)
|
|
90
105
|
@click.option("--data", "data_input", required=True, help="JSON data (value, - for stdin, @file).")
|
|
91
106
|
@click.option(
|
|
92
107
|
"--values-type",
|
|
@@ -179,7 +194,13 @@ def asset_update(ctx, asset_id, data_input, dry_run):
|
|
|
179
194
|
@click.option("--dry-run", is_flag=True, help="Preview without updating.")
|
|
180
195
|
@pass_context
|
|
181
196
|
def asset_update_content(
|
|
182
|
-
ctx,
|
|
197
|
+
ctx,
|
|
198
|
+
asset_id,
|
|
199
|
+
data_input,
|
|
200
|
+
values_type,
|
|
201
|
+
file_pairs,
|
|
202
|
+
content_types,
|
|
203
|
+
dry_run,
|
|
183
204
|
):
|
|
184
205
|
"""Update asset content (Slate JSON or markdown), optionally uploading files.
|
|
185
206
|
|
|
@@ -228,7 +249,9 @@ def asset_update_content(
|
|
|
228
249
|
@asset_group.command("upload-file")
|
|
229
250
|
@click.option("--asset-id", required=True, help="Document ID to upload into.")
|
|
230
251
|
@click.option(
|
|
231
|
-
"--field",
|
|
252
|
+
"--field",
|
|
253
|
+
"field_name",
|
|
254
|
+
required=True,
|
|
232
255
|
help="File-block name (options.name) on the Slate tree.",
|
|
233
256
|
)
|
|
234
257
|
@click.option(
|
|
@@ -361,7 +384,11 @@ def asset_add_task(ctx, asset_id, title, description, dry_run):
|
|
|
361
384
|
data["description"] = description
|
|
362
385
|
|
|
363
386
|
_execute_asset(
|
|
364
|
-
ctx,
|
|
387
|
+
ctx,
|
|
388
|
+
"add_task_definition",
|
|
389
|
+
asset_id=asset_id,
|
|
390
|
+
data=data,
|
|
391
|
+
dry_run=dry_run,
|
|
365
392
|
)
|
|
366
393
|
|
|
367
394
|
|
|
@@ -379,7 +406,8 @@ def asset_add_task(ctx, asset_id, title, description, dry_run):
|
|
|
379
406
|
"values_file",
|
|
380
407
|
default=None,
|
|
381
408
|
type=click.Path(exists=True, dir_okay=False),
|
|
382
|
-
help="Path to
|
|
409
|
+
help="Path to the body file. With --values-type markdown the file is sent "
|
|
410
|
+
"as a raw string; otherwise it is parsed as a Slate JSON array.",
|
|
383
411
|
)
|
|
384
412
|
@click.option(
|
|
385
413
|
"--values-type",
|
|
@@ -390,7 +418,13 @@ def asset_add_task(ctx, asset_id, title, description, dry_run):
|
|
|
390
418
|
@click.option("--dry-run", is_flag=True, help="Preview without updating.")
|
|
391
419
|
@pass_context
|
|
392
420
|
def asset_set_task_values(
|
|
393
|
-
ctx,
|
|
421
|
+
ctx,
|
|
422
|
+
asset_id,
|
|
423
|
+
task_definition_id,
|
|
424
|
+
data_input,
|
|
425
|
+
values_file,
|
|
426
|
+
values_type,
|
|
427
|
+
dry_run,
|
|
394
428
|
):
|
|
395
429
|
"""Set the body of a single task definition (markdown or SlateJS).
|
|
396
430
|
|
|
@@ -403,30 +437,9 @@ def asset_set_task_values(
|
|
|
403
437
|
upscaler asset set-task-values --asset-id rd_abc --task-id td_xyz \\
|
|
404
438
|
--data '{"values": "## Header\\n..."}' --values-type markdown
|
|
405
439
|
"""
|
|
406
|
-
from src.cli.helpers import
|
|
440
|
+
from src.cli.helpers import build_values_payload
|
|
407
441
|
|
|
408
|
-
|
|
409
|
-
click.echo("Provide either --data or --values-file.", err=True)
|
|
410
|
-
sys.exit(1)
|
|
411
|
-
return
|
|
412
|
-
if data_input and values_file:
|
|
413
|
-
click.echo("--data and --values-file are mutually exclusive.", err=True)
|
|
414
|
-
sys.exit(1)
|
|
415
|
-
return
|
|
416
|
-
|
|
417
|
-
if values_file:
|
|
418
|
-
data = {"values": Path(values_file).read_text()}
|
|
419
|
-
else:
|
|
420
|
-
try:
|
|
421
|
-
data = parse_data(data_input)
|
|
422
|
-
except Exception as e:
|
|
423
|
-
click.echo(str(e), err=True)
|
|
424
|
-
sys.exit(1)
|
|
425
|
-
return
|
|
426
|
-
if "values" not in data:
|
|
427
|
-
click.echo("--data must include a 'values' key.", err=True)
|
|
428
|
-
sys.exit(1)
|
|
429
|
-
return
|
|
442
|
+
data = build_values_payload(data_input, values_file, values_type)
|
|
430
443
|
|
|
431
444
|
data["taskDefinitionId"] = task_definition_id
|
|
432
445
|
if values_type:
|
|
@@ -488,10 +501,164 @@ def asset_set_task_condition(ctx, asset_id, task_definition_id, data_input, dry_
|
|
|
488
501
|
)
|
|
489
502
|
|
|
490
503
|
|
|
504
|
+
@asset_group.command("add-lesson")
|
|
505
|
+
@click.option("--asset-id", required=True, help="Course definition ID (cd_...).")
|
|
506
|
+
@click.option("--title", required=True, help="Lesson title.")
|
|
507
|
+
@click.option("--description", default=None, help="Lesson description (optional).")
|
|
508
|
+
@click.option("--dry-run", is_flag=True, help="Preview without creating.")
|
|
509
|
+
@pass_context
|
|
510
|
+
def asset_add_lesson(ctx, asset_id, title, description, dry_run):
|
|
511
|
+
"""Append a new lesson definition to a course_definition.
|
|
512
|
+
|
|
513
|
+
Returns the assigned lesson definition ID (cl_...) which is needed to set
|
|
514
|
+
the lesson's body via `set-lesson-values`, rename it, or reorder it.
|
|
515
|
+
|
|
516
|
+
Example:
|
|
517
|
+
upscaler asset add-lesson --asset-id cd_abc --title "Phishing Basics"
|
|
518
|
+
"""
|
|
519
|
+
data = {"title": title}
|
|
520
|
+
if description:
|
|
521
|
+
data["description"] = description
|
|
522
|
+
|
|
523
|
+
_execute_asset(
|
|
524
|
+
ctx,
|
|
525
|
+
"add_lesson_definition",
|
|
526
|
+
asset_id=asset_id,
|
|
527
|
+
data=data,
|
|
528
|
+
dry_run=dry_run,
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
@asset_group.command("set-lesson-title")
|
|
533
|
+
@click.option("--asset-id", required=True, help="Course definition ID (cd_...).")
|
|
534
|
+
@click.option("--lesson-id", "lesson_definition_id", required=True, help="Lesson ID (cl_...).")
|
|
535
|
+
@click.option("--title", required=True, help="New lesson title.")
|
|
536
|
+
@click.option("--dry-run", is_flag=True, help="Preview without updating.")
|
|
537
|
+
@pass_context
|
|
538
|
+
def asset_set_lesson_title(ctx, asset_id, lesson_definition_id, title, dry_run):
|
|
539
|
+
"""Rename a single lesson definition."""
|
|
540
|
+
_execute_asset(
|
|
541
|
+
ctx,
|
|
542
|
+
"set_lesson_definition_title",
|
|
543
|
+
asset_id=asset_id,
|
|
544
|
+
data={"lessonDefinitionId": lesson_definition_id, "title": title},
|
|
545
|
+
dry_run=dry_run,
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
@asset_group.command("set-lesson-description")
|
|
550
|
+
@click.option("--asset-id", required=True, help="Course definition ID (cd_...).")
|
|
551
|
+
@click.option("--lesson-id", "lesson_definition_id", required=True, help="Lesson ID (cl_...).")
|
|
552
|
+
@click.option("--description", required=True, help="New lesson description.")
|
|
553
|
+
@click.option("--dry-run", is_flag=True, help="Preview without updating.")
|
|
554
|
+
@pass_context
|
|
555
|
+
def asset_set_lesson_description(ctx, asset_id, lesson_definition_id, description, dry_run):
|
|
556
|
+
"""Set the description of a single lesson definition."""
|
|
557
|
+
_execute_asset(
|
|
558
|
+
ctx,
|
|
559
|
+
"set_lesson_definition_description",
|
|
560
|
+
asset_id=asset_id,
|
|
561
|
+
data={"lessonDefinitionId": lesson_definition_id, "description": description},
|
|
562
|
+
dry_run=dry_run,
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
@asset_group.command("set-lesson-values")
|
|
567
|
+
@click.option("--asset-id", required=True, help="Course definition ID (cd_...).")
|
|
568
|
+
@click.option("--lesson-id", "lesson_definition_id", required=True, help="Lesson ID (cl_...).")
|
|
569
|
+
@click.option(
|
|
570
|
+
"--data",
|
|
571
|
+
"data_input",
|
|
572
|
+
default=None,
|
|
573
|
+
help="JSON object with 'values' key. Mutually exclusive with --values-file.",
|
|
574
|
+
)
|
|
575
|
+
@click.option(
|
|
576
|
+
"--values-file",
|
|
577
|
+
"values_file",
|
|
578
|
+
default=None,
|
|
579
|
+
type=click.Path(exists=True, dir_okay=False),
|
|
580
|
+
help="Path to the body file. With --values-type markdown the file is sent "
|
|
581
|
+
"as a raw string; otherwise it is parsed as a Slate JSON array.",
|
|
582
|
+
)
|
|
583
|
+
@click.option(
|
|
584
|
+
"--values-type",
|
|
585
|
+
type=click.Choice(["markdown", "slateJson"]),
|
|
586
|
+
default=None,
|
|
587
|
+
help="Content format: 'markdown' or 'slateJson' (default: slateJson).",
|
|
588
|
+
)
|
|
589
|
+
@click.option("--dry-run", is_flag=True, help="Preview without updating.")
|
|
590
|
+
@pass_context
|
|
591
|
+
def asset_set_lesson_values(
|
|
592
|
+
ctx,
|
|
593
|
+
asset_id,
|
|
594
|
+
lesson_definition_id,
|
|
595
|
+
data_input,
|
|
596
|
+
values_file,
|
|
597
|
+
values_type,
|
|
598
|
+
dry_run,
|
|
599
|
+
):
|
|
600
|
+
"""Set the body of a single lesson definition (markdown or SlateJS).
|
|
601
|
+
|
|
602
|
+
Provide content via either --data (JSON object with 'values') or --values-file
|
|
603
|
+
(raw markdown or Slate JSON file). Pass --values-type markdown for markdown bodies.
|
|
604
|
+
|
|
605
|
+
Examples:
|
|
606
|
+
upscaler asset set-lesson-values --asset-id cd_abc --lesson-id cl_xyz \\
|
|
607
|
+
--values-file lesson1-body.md --values-type markdown
|
|
608
|
+
"""
|
|
609
|
+
from src.cli.helpers import build_values_payload
|
|
610
|
+
|
|
611
|
+
data = build_values_payload(data_input, values_file, values_type)
|
|
612
|
+
|
|
613
|
+
data["lessonDefinitionId"] = lesson_definition_id
|
|
614
|
+
if values_type:
|
|
615
|
+
data["valuesType"] = values_type
|
|
616
|
+
|
|
617
|
+
_execute_asset(
|
|
618
|
+
ctx,
|
|
619
|
+
"set_lesson_definition_values",
|
|
620
|
+
asset_id=asset_id,
|
|
621
|
+
data=data,
|
|
622
|
+
dry_run=dry_run,
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
@asset_group.command("remove-lesson")
|
|
627
|
+
@click.option("--asset-id", required=True, help="Course definition ID (cd_...).")
|
|
628
|
+
@click.option("--lesson-id", "lesson_definition_id", required=True, help="Lesson ID (cl_...).")
|
|
629
|
+
@click.option("--dry-run", is_flag=True, help="Preview without removing.")
|
|
630
|
+
@pass_context
|
|
631
|
+
def asset_remove_lesson(ctx, asset_id, lesson_definition_id, dry_run):
|
|
632
|
+
"""Remove a single lesson definition from a course_definition."""
|
|
633
|
+
_execute_asset(
|
|
634
|
+
ctx,
|
|
635
|
+
"remove_lesson_definition",
|
|
636
|
+
asset_id=asset_id,
|
|
637
|
+
data={"lessonDefinitionId": lesson_definition_id},
|
|
638
|
+
dry_run=dry_run,
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
@asset_group.command("move-lesson")
|
|
643
|
+
@click.option("--asset-id", required=True, help="Course definition ID (cd_...).")
|
|
644
|
+
@click.option("--from-id", "from_id", required=True, help="Lesson ID to move (cl_...).")
|
|
645
|
+
@click.option("--to-id", "to_id", required=True, help="Target sibling lesson ID (cl_...).")
|
|
646
|
+
@click.option("--dry-run", is_flag=True, help="Preview without reordering.")
|
|
647
|
+
@pass_context
|
|
648
|
+
def asset_move_lesson(ctx, asset_id, from_id, to_id, dry_run):
|
|
649
|
+
"""Reorder a lesson definition within a course_definition."""
|
|
650
|
+
_execute_asset(
|
|
651
|
+
ctx,
|
|
652
|
+
"move_lesson_definition",
|
|
653
|
+
asset_id=asset_id,
|
|
654
|
+
data={"fromId": from_id, "toId": to_id},
|
|
655
|
+
dry_run=dry_run,
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
|
|
491
659
|
def _execute_asset(ctx, operation, asset_id=None, asset_type=None, data=None, dry_run=False):
|
|
492
660
|
"""Execute an asset operation."""
|
|
493
|
-
from src.cli.helpers import make_client
|
|
494
|
-
from src.formatters.json_fmt import format_json
|
|
661
|
+
from src.cli.helpers import emit_action_result, emit_dry_run, make_client
|
|
495
662
|
|
|
496
663
|
payload = {"operation": operation}
|
|
497
664
|
if asset_id:
|
|
@@ -502,11 +669,11 @@ def _execute_asset(ctx, operation, asset_id=None, asset_type=None, data=None, dr
|
|
|
502
669
|
payload["data"] = data
|
|
503
670
|
|
|
504
671
|
if dry_run:
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
672
|
+
emit_dry_run(
|
|
673
|
+
ctx,
|
|
674
|
+
summary=f"would {operation} {asset_type or asset_id or 'asset'}",
|
|
675
|
+
payload=payload,
|
|
676
|
+
)
|
|
510
677
|
return
|
|
511
678
|
|
|
512
679
|
client = make_client(ctx)
|
|
@@ -517,11 +684,9 @@ def _execute_asset(ctx, operation, asset_id=None, asset_type=None, data=None, dr
|
|
|
517
684
|
handle_error(ctx, e)
|
|
518
685
|
return
|
|
519
686
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
d = result.get("data", {})
|
|
524
|
-
click.echo(f"Asset {operation}: {d.get('assetId', d.get('asset_id', ''))}")
|
|
687
|
+
emit_action_result(
|
|
688
|
+
ctx, result, label=f"Asset {operation}", id_keys=("assetId", "asset_id", "id")
|
|
689
|
+
)
|
|
525
690
|
|
|
526
691
|
|
|
527
692
|
# File-upload flow for Pattern A (Documents). Documents persist `values` as
|
|
@@ -678,9 +843,7 @@ def _dry_run_asset_with_files(ctx, asset_id, files, data):
|
|
|
678
843
|
if ctx.json_mode:
|
|
679
844
|
click.echo(format_json({"dry_run": True, "payload": dry_payload}, compact=True))
|
|
680
845
|
else:
|
|
681
|
-
click.echo(
|
|
682
|
-
f"[dry-run] Would upload {len(files)} file(s) and update-content asset:"
|
|
683
|
-
)
|
|
846
|
+
click.echo(f"[dry-run] Would upload {len(files)} file(s) and update-content asset:")
|
|
684
847
|
click.echo(format_json(dry_payload))
|
|
685
848
|
|
|
686
849
|
|
|
@@ -388,6 +388,7 @@ def status(ctx):
|
|
|
388
388
|
return
|
|
389
389
|
|
|
390
390
|
mode = "device" if token_data.client_id == "device" else "oauth"
|
|
391
|
+
refresh_present = bool(token_data.refresh_token)
|
|
391
392
|
|
|
392
393
|
if ctx.json_mode:
|
|
393
394
|
expires_in = int(token_data.expires_at - time.time())
|
|
@@ -397,11 +398,21 @@ def status(ctx):
|
|
|
397
398
|
"expires_in": max(0, expires_in),
|
|
398
399
|
"organization_id": token_data.organization_id,
|
|
399
400
|
"mode": mode,
|
|
401
|
+
"refresh_token_present": refresh_present,
|
|
400
402
|
}))
|
|
401
403
|
else:
|
|
404
|
+
expires_in = int(token_data.expires_at - time.time())
|
|
402
405
|
click.echo(f"Status: Authenticated ({mode})")
|
|
403
406
|
if token_data.organization_id:
|
|
404
407
|
click.echo(f"Organization: {token_data.organization_id}")
|
|
408
|
+
if expires_in <= 0:
|
|
409
|
+
click.echo("Token: expired (run: upscaler refresh)")
|
|
410
|
+
else:
|
|
411
|
+
click.echo(f"Expires in: {_format_duration(expires_in)}")
|
|
412
|
+
if refresh_present:
|
|
413
|
+
click.echo("Refresh token: present")
|
|
414
|
+
else:
|
|
415
|
+
click.echo("Refresh token: missing (re-login required on expiry)")
|
|
405
416
|
|
|
406
417
|
|
|
407
418
|
@click.command()
|